belongs_toとhas_manyの意味としてはこちらの記事で扱っているので、
こちらを前提として進めていきます。
学習中ですので間違えている箇所があると思いますが、ご容赦ください
usersテーブルはすでに作成されており、schemaに新たにrelationshipsテーブルを作成します。
create_table "relationships", force: :cascade do |t|
t.integer "following_id", null: false
t.integer "follower_id", null: false
:
end
create_table "users", force: :cascade do |t|
:
t.string "name"
t.text "introduction"
:
end
今回はuser同士をどう紐づけるかということで悩みました。
今まではuserモデルとbookモデルを1対多の関係でbelongs_toとhas_manyを使っていましたが、今回は同じモデル同士での紐付けとなるので、どこかしらで矛盾が発生し、表現が難しくなります。
そこで今回は、中間テーブルという仮のテーブルを用意し、Userとrelashionshipsを1対多の関係で結び、
user同士に関係を持たせるという表現になります。
ここではUserを大まかに2つに分けています
- フォローされる側
- フォローする側
relationshipモデルにに記述する際は、以下のように記述します。
belongs_to :following, class_name: 'User'
belongs_to :follower, class_name: 'User'
belongs_toが参照する先は、モデル(テーブルを作成するときに作ったモデル)名を参照し、合致するモデル名があれば
モデルに対応したテーブルを参照します。
followingとfollowerが指定されていますが、合致するモデル名は存在ません。
relationshipsテーブルの中にfollower、followingのカラムがありますが、それは検索対象ではありませんので、
nil(何もない)が帰ってくると思います。⇦多分
先ほど、この二つのレコードはどちらもUserを二つに分割したもの
そもそもの実態はUserです。
ですので、後ろのclass_nameで、実態はどのモデルに格納されているのかを指定してあげています。
この場合followerとfollowingのどちらもUserが本体ですので、Userモデルを参照先として指定しています。
has_many :relationships, foreign_key: :following_id
has_many :followings, through: :relationships, source: :follower
has_many :reverse_of_relationships, class_name: 'Relationship', foreign_key: :follower_id
has_many :followers, through: :reverse_of_relationships, source: :following
こちらはUserモデルです。
userは複数のrelationship(followerもしくはfollowing)のデータを持っており、
foreign_keyで、relationshipsテーブルに格納されているfollowerもしくはfollowingは
どちらのUsersを持つのかを指定しています。
つまり、relationships(users)が、followerとfollowingのどちらに属しているとするのかを指定しています。
reverse_of_relationshipsは、人間が勝手につけた仮のテーブル名で、先ほどのbelongs_to :followerと同じように参照先が存在しません。
ですので、class_nameにモデル名を指定して、そこが本体のモデル名だと指定する必要があります。
※ここで同じ名前にしないのは、変数と同じような問題になることを防ぐためです。
through
こちらは中間テーブルを介してデータの取得ができるというものです。throughを使うと、中間テーブルと関連づいたテーブルの参照ができます。
例えば
has_many :followings, through: :relationships, source: :follower
を見てみます。
relationshipsテーブルと関連づいたカラムには、followerとfollowingがありました。
throughを使うことで中間テーブルと関連づいたテーブルデータの参照ができます。
この場合、userは複数のfollowingを持っており、参照先であるfollowingsテーブルは存在しませんが、relationshipsテーブルにはカラムとして存在します。
relationshipsにあるデータを取得するためにthroughでrelationshipsテーブルを参照先に指定してあります。
今の状況を図で表すとこのようになります。
sourceは、
[あるuserがフォローしている人(複数)=あるuserにフォローされている人]
という関係があるので、
中間テーブルを介して、[あるユーザーにフォローされている人(follower)]を取ってこれるようになります。
has_many :followers, through: :reverse_of_relationship, source: :following
これも
[あるユーザーのフォロワー(複数)=あるユーザーをフォローしている人]の関係があるので、
中間テーブルを介して、[あるユーザーをフォローしている人(following)]を取ってこれるようになります。
参考文献