基本はhas_many
とbelongs_to
User/Relationshipの関連付けは、以下の条件が基本となります。
- 1人のユーザーには1対多(
has_many
)のリレーションシップが存在する - 当該リレーションシップには、フォローしているユーザーとフォローされているユーザー両方が属する(
belongs_to
)
最終的には、「user
とactive_relationship
に関して以下のようなメソッドが使えるようになる」というのが完成像です。
active_relationship.user
user.active_relationships
user.active_relationships.create(followed_id: ...)
user.active_relationships.create!(followed_id: ...)
user.active_relationships.build(followed_id: ...)
user.active_relationships.find_by(id: 1)
「モデル名と関係名が異なる」という要件
しかしながら、今回は「Relationshipモデルに対してactive_relationship
(ならびにpassive_relationship
)という関係性を定義する」というのが要件です。1対多の関係の多側において、モデル名と関係名が異なります。
「モデル名と関係名が異なる」という要件の存在ゆえに、Userモデルにおけるhas_many
、およびRelationshipモデルにおけるbelongs_to
の記法は、モデル名と関係名が同じ場合とは異なります。
1対多の1側において、多側のモデル名と関係名が異なる場合のhas_many
の書き方
まずはUserモデルにおけるhas_many
の書き方についてです。
- has_many :active_relationships
上記のような書き方では、active_relationships
という関係性を正しく定義できません。「ActiveRelationshipモデルを参照しようとしてしまい、Relationshipモデルを参照できない」ためです。「Relationshipモデルを参照する」ということを、has_many
に明確に渡さなければなりません。
「特定のモデルを参照する」ということをhas_many
に渡すためには、class_name
というオプションハッシュに、当該モデルのクラス名を文字列として渡せばOKです。Relationshipモデルのクラス名は"Relationship"
なので、今回の事例の場合、has_many
の書き方は以下のようになります。
has_many :active_relationships, class_name: "Relationship"
1対多の多側において、外部キー名と対応するモデル名が異なる場合のbelongs_to
の書き方
続いてはRelathionshipモデルにおけるbelongs_to
の書き方についてです。前提条件は以下です。
-
active_relationship
という関係性は、relationships
テーブルのfollowed_id
カラムに紐付けしたい -
followed_id
は外部キーであり、その参照先はUserモデルのid
カラムである
このような関係性をRelationshipモデルに定義したい場合、belongs_to
の書き方は以下のようになります。
belongs_to :followed, class_name: "User"
Relationshipモデルのfollowed_id
に関係性を紐付けるので、第1引数に与えるシンボルは:followed
となります。
オプションハッシュclass_name
に、参照先モデルのクラス名を文字列として与えています。Userモデルのクラス名はUser
であるため、今回だと"User"
という文字列である…という次第ですね。
上記を踏まえ、UserモデルおよびRelationshipモデルに必要な変更の全体像はどうなるか
Userモデルに必要な変更の全体像
class User < ApplicationRecord
has_many :microposts, dependent: :destroy
+ has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
...略
end
第1引数の:active_relationships
とclass_name
オプション、foreign_key
オプションの説明は上記のとおりです。ユーザーが削除されたら関連するリレーションシップも削除されるべきであるので、dependent: :destroy
というオプションを与えています。
Relationshipモデルに必要な変更の全体像
class Relationship < ApplicationRecord
+ belongs_to :follower, class_name: "User"
+ belongs_to :followed, class_name: "User"
end
belongs_to :follower
のほうは、あとで実装する「受動的関係(passive_relationship
)で用いる準備としての実装であり、現時点で直ちに使うものではありません。
これらの実装により使えるようになるメソッド
ここまでの実装により、active_relationships
に関する以下のメソッドを使えるようになります。
メソッド | 用途 |
---|---|
active_relationship.follower |
フォロワーを返す |
active_relationship.followed |
フォローしているユーザーを返す |
user.active_relationships.create(followed_id: other_id) |
user と紐付いた能動的関係を作成し、RDBに保存する(失敗時にnil を返す) |
user.active_relationships.create!(followed_id: other_id) |
user と紐付いた能動的関係を作成し、RDBに保存する(失敗時にエラーを返す) |
user.active_relationships.build(followed_id: other_id) |
user と紐付いた新しいRelationshipオブジェクトを返す(この時点ではRDBには保存されない) |