has_many throughで,class_nameとかforeign_keyをちゃんと復習してみる

has_many,throughとかでモデルやテーブルの構成を考えるとき,どうしてもカラム名と関連づけるテーブルの名前が一致しなかったり,望み通りの命名で行けない場合がある.


もちろん,一つのテーブルが,他のテーブルのレコードを複数持つだけなら,命名はスマートに決められるのだけれど.



今回の例題は,Userモデルに対するフォロー・フォロワーを実現すること.

Userは一つのテーブルでなければならないことはわかりますよね.
ということは,UserテーブルがUserFollowという中間テーブルを介して,Userテーブルのレコードをhas_manyするという形をとる.

もうガリガリと書いていきます.
まず,こういう,元から名前の統一が難しそうな場合はテーブルを先に作ってしまう.

Userテーブル

id name
1 poteto
2 hoge


UserFollowテーブル

id user_id follow_id
1 1 2

これで,potetoさんがhogeさんをフォローしているという状態が実現できる.



続いてモデルの定義.

user.rb

class User < ActiveRecord::Base
  has_many :user_follows, class_name: "UserFollow", foreign_key: :user_id
  has_many :follows, through: :user_follows
  has_many :user_followers, class_name: "UserFollow", foreign_key: :follow_id
  has_many :followers, through: :user_followers
end

user_follow.rb

class UserFollow < ActiveRecord::Base
  belongs_to :follow, class_name: "User", foreign_key: follow_id
  belongs_to :follower, class_name: "USer", foreign_key: user_id
end

こんな感じになりますよね.


class_nameは対象となるモデルを指定して,foreign_keyで,どのカラムの値が一致するレコードを取得するのかを指定します.

user_followsに関して言うなら,あるuserのid値が,UserFollowのuser_idカラムの値と一致するレコードを取り出します.
followsに関しては,そのuser_followsをthroughするわけですね.

ではfollowsはどこで指定されるのか?
UserFollowの定義で指定されます.

belongs_to :follow, class_name: "User", foreign_key: follow_id

このようにして,今度は,選択されたUserFollowのレコードのfollow_idの値が,class_nameで指定されたUserテーブルのid値と一致する,Userテーブルのレコードを,followとして抜き出します.

Userモデルはそれをhas_manyするのでfollowsという複数形になっているわけです.



逆向き(followers)も同様ですね.

この辺を上手く使いこなすと,いい感じに多対多の関連を作ることができます.