0
1

More than 1 year has passed since last update.

belongs_toとhas_manyを使ったDBへの参照先指定(アソシエーション)

Last updated at Posted at 2022-06-29

belongs_toとhas_manyの意味としてはこちらの記事で扱っているので、
こちらを前提として進めていきます。

学習中ですので間違えている箇所があると思いますが、ご容赦ください:bow_tone1:

usersテーブルはすでに作成されており、schemaに新たにrelationshipsテーブルを作成します。

schema.rb
 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つに分けています

  • フォローされる側
  • フォローする側
どちらもUserです。

relationshipモデルにに記述する際は、以下のように記述します。

relationship.rb
  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モデルを参照先として指定しています。

user.rb
  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テーブルを参照先に指定してあります。

スクリーンショット 2022-06-30 3.49.40.png

今の状況を図で表すとこのようになります。

sourceは、
[あるuserがフォローしている人(複数)=あるuserにフォローされている人]
という関係があるので、
中間テーブルを介して、[あるユーザーにフォローされている人(follower)]を取ってこれるようになります。

has_many :followers, through: :reverse_of_relationship, source: :following

これも
[あるユーザーのフォロワー(複数)=あるユーザーをフォローしている人]の関係があるので、
中間テーブルを介して、[あるユーザーをフォローしている人(following)]を取ってこれるようになります。

参考文献

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1