LoginSignup
1
4

More than 3 years have passed since last update.

【Rials】フォロー機能1~多対多のモデルの構造

Last updated at Posted at 2019-07-19

Rialsでのフォロー機能についてのメモです。
ツイッタークローンのフォロー機能のモデルの説明です。

こちらの続きです。

フォロー機能の概要

投稿機能は1対多で、
フォロー機能は、モデルの多対多の関係で成り立ちます。

投稿機能では、ユーザ(1)に対して、投稿(多)という関係が成り立ちました。
フォロー機能では、ユーザ(多)に対して、ユーザ(多)という構造を取ります。
「ユーザって言っても、自分は一人やし、1対多なんじゃないの?」と疑問に思われる方もいらっしゃるかと思いますが、半分合ってます!

前回も使わせてもらいましたが、こちらの記事を参考にしてみてください。めっちゃ分かりやすいです。
https://qiita.com/kazukimatsumoto/items/14bdff681ec5ddac26d1

多対多のモデルの構造の場合、中間にテーブルを新しく作成し、相互のやり取りをするのが一般的です。

全体像

モデル

User(ユーザ)
name:string amail:string password_digest:string

Relationship (中間テーブル)
user:references follow:references

ユーザ同士のフォロー機能なので、中間テーブルをはさむことによって、
モデルの構造はUser-Relationship-Userのようになります。
User(フォローする側)-Relationship-User(フォローされる側)と分けることが出来ます。
もう一つ細かく分けると、
User(フォローする側)-(フォローするための中間テーブル)Relationship(フォローされるための中間テーブル)-User(フォローされる側)
となります。

モデルの構成で表してみると、
User(フォローする側)-(user)Relationship(follow)-User(フォローされる側)
こうなります。

User(フォローする側)-(user)Relationship
User(フォローされる側)-(follow)Relationship
とも捉えることができます。(捉え方多すぎ!笑)

こうすると1対多+1対多となっているように見えてきませんか?
実は、多対多は、中間テーブル(多)を挟んだ1対多+1対多なのです!

モデルの作成

上記の構成を参考に、モデルの作成をしましょう。

マイグレーションファイルでの設定

中間テーブルのモデルの下記のコードのマイグレーションファイルが出来上がっているかと思います。

db/migrate/年月日時_create_relationships.rb
class CreateRelationships < ActiveRecord::Migration[5.0]
  def change
    create_table :relationships do |t|
      t.references :user, foreign_key: true
      t.references :follow, foreign_key: true

      t.timestamps
    end
  end
end

userもfollowもUserテーブルに紐づけたいです。
railsでは、別のテーブル名をモデルのカラム名に設定すると、自動で参照してくれます。なので、t.references :user は、Userテーブルを参照することが出来ます。

t.references :followをUserテーブルに紐づけたい。。
そんなときは、

t.references :follow, foreign_key: { to_table: :users }

こう記述すればfollowをUserテーブルに紐づけることが出来ます!
下記のコードに書き換えます。

db/migrate/年月日時_create_relationships.rb
class CreateRelationships < ActiveRecord::Migration[5.0]
  def change
    create_table :relationships do |t|
      t.references :user, foreign_key: true
      t.references :follow, foreign_key: { to_table: :users }

      t.timestamps

      t.index [:user_id, :follow_id], unique: true
    end
  end
end

ここで、

t.index [:user_id, :follow_id], unique: true

というコードを追記しています。unique: trueを記述することで、フォローとフォロワーがごっちゃにならないよにしています。

モデル

モデルのコードを見てみましょう。

まず、中間テーブルから。

app/models/relationship.rb
class Relationship < ApplicationRecord
  belongs_to :user
  belongs_to :follow, class_name: 'User'
end

多対多の構造は、細かく見ていくと
1対多+1対多
だと説明しました。

なので、relationshipテーブル(中間テーブル)では、haa_manyではなく、belongs_to でUserテーブルと紐づけてます。Userテーブルで、has_manyでrelationshipテーブルと紐づけることで多対他になります。

また、Userテーブルと各カラムを紐づけたいのですが、このままの状態ではuserカラムしか紐づいていないです。(railsの機能でカラムと同じ名前のテーブルを探しにいくため)
belongs_to :follow, class_name: 'User'ですが、class_name: 'User'と追加で指定することで、followカラムもUserテーブルを参照することが出来るようになります。

Userテーブルを見てみましょう!

app/models/user.rb
class User < ApplicationRecord

  has_many :microposts

  has_many :relationships
  has_many :followings, through: :relationships, source: :follow
  has_many :reverses_of_relationship, class_name: 'Relationship', foreign_key: 'follow_id'
  has_many :followers, through: :reverses_of_relationship, source: :user
end

has_many :microposts は、投稿機能を追加したときに作成したコードです。

下の4つについて見ていきましょう。

has_many :relationshipsで、relationshipsテーブルから、データを取得してくることが出来ます。1対多の関係です。
自分がフォローしているユーザを取得します。
railsでは、同じ名前のテーブル・カラムがあると自動で取得してくれるので、RelationテーブルにUserカラムの情報を取得していることになります。

一行飛ばして、

has_many :reverses_of_relationship, class_name: 'Relationship', foreign_key: 'follow_id'

を見てみましょう。

reverses_of_relationshipというのは自由に決められる名前です。

class_name: 'Relationship'では、Relationshipテーブルからデータを取ってきてるということを表しています。
そして、foreign_key: 'follow_id'でfollow_idのデータですよ!というようになります。

has_many :reverses_of_relationship, class_name: 'Relationship', foreign_key: 'follow_id'

では、reverses_of_relationshipという名前の箱に、Relationshipテーブルを通して、follow_idのデータが入ると言えます。

では、

has_many :followings, through: :relationships, source: :follow


has_many :followers, through: :reverses_of_relationship, source: :user
end

について見ていきます。

throughとsourceがミソになってきます。

through: :中間テーブル, source: :カラム名
で、中間テーブルに設定されているカラムのデータを引っ張てくることが出来ます。

今回のように、ユーザがフォローしているユーザを取得したければ、中間テーブルを通過することで、

has_many :followings, through: :relationships, source: :follow
は、ユーザ(user)がフォローしているユーザ(followings)を取得

has_many :followers, through: :reverses_of_relationship, source: :user
end

で、ユーザ(User)のことをフォローしているユーザ(followers)を取得することが出来ます。

例えば、ユーザ(user)がフォローしているユーザ(followings)を取得したい場合、

    @user = User.find(params[:id])
    @followings = @user.followings.page(params[:page])

というような記述の仕方も可能になります。

1
4
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
1
4