Help us understand the problem. What is going on with this article?

Railsチュートリアル 第14章 ユーザーをフォローする - Relationshipモデル - User/Relationshipの関連付け

基本はhas_manybelongs_to

User/Relationshipの関連付けは、以下の条件が基本となります。

  • 1人のユーザーには1対多(has_many)のリレーションシップが存在する
  • 当該リレーションシップには、フォローしているユーザーとフォローされているユーザー両方が属する(belongs_to)

最終的には、「useractive_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モデルに必要な変更の全体像

app/models/user.rb
  class User < ApplicationRecord
    has_many :microposts, dependent: :destroy
+   has_many :active_relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
    ...略
  end

第1引数の:active_relationshipsclass_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には保存されない)
rapidliner00
* 現在のところは、エンジニアに憧れる非エンジニア * エンジニア的な業務効率化・改善に興味あり
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした