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

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

[こちら](https://qiita.com/gussan-dayo/items/434fd0e08d67b14471fa)の続きです。

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

投稿機能では、ユーザ(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`` は、投稿機能を追加したときに作成したコードです。

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

``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])
```

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