自己参照の関連
Railsのサンプルってほぼ必ずミニブログ=Twitterなんだけど、Twitterのfollowing(フォローの対象となった人)/follower(フォローしている側)のリレーションって考えると意外とめんどくさい。
ざっくり、UserとUserが多対多で結ばれることは想像がつくが、その中間はどう実装すればいいのか。。。
答えはこちら。
◯app/models/user.rb
class User < ActiveRecord::Base has_many :following_ships, foreign_key: :follwer_id has_many :followings, through: :following_ships, class_name: "User" has_many :inverse_following_ships, foreign_key: :following_id, class_name: "FollowingShip" has_many :followers, through: :inverse_following_ships, class_name: "User" end class FollowingShip < ActiveRecord::Base belongs_to :follower, class_name: "User" belongs_to :following, class_name: "User" end
テーブルはusersとfollowing_ships, クラスもUserとFollowingShipの2つしかないわけだが、関係を二重に定義づけることが重要。inverse_following_shipsは答えをしらないとなかなか思いつかないと思う。
ここでオプションの確認をすると、foreign_keyは関連における参照元の外部キーを指定するもの。ちょっと混同してたんだけど参照元というのはこの場合followingshipのほうで、一般にA has_many BsだとAが参照先、Bが参照元。「A has many Bs」という文章からは逆のような気もするけど、キーを持っているのはBのほうなのだから確かにBが参照元である。
で、通常だとこのキーはuser_idになるんだけど、今回はUserと二重に対応させるのだからuserの外部キーが2つ必要。これをfollower_idとfollowing_idとした。
で、@userが自分のid=follower_idであるFollowingShipを集めると、それは自分がフォローしている側のfollowingshipが集められる。つまり外部キーをfollower_idとした場合の関連はfollowingsである。
一方で、@userがid=following_idであるFollowingShipを集めると、それは自分のことをフォローしてくれているfollowingshipが集められる。この場合の関連はfollowersである。
どちらの関連も必要なのだけど、has_many :following_ship, foreign_key: [:follower_id, :following_id]のようなことはできないから、後者はクラスは同じだが異なる関連として定義する。
これで、@user.followersと@user.followingsでそれぞれフォロワーとフォローしている人を取得できるようになりました。
◯参考記事
ほぼそのままです。
http://asciicasts.com/episodes/163-self-referential-association