[Rails]フォロー、フォロワー機能

SNSの必須機能として、フォロー・フォロワー機能を実装していきます。

中間テーブルの理解が難しいとは思いますが、できるだけわかりやすく解説していきます。

#開発環境

> ruby 2.6.3
> Rails 5.2.6
> Bootstrap 4.5

#前提

今回は、フォロー・フォロワー機能の実装のみの解説なので、deviseでユーザー認証が実装されている前提で話を進めていきます。

モデルコントローラーは以下
relationshipモデル
relationshipsコントローラー

#手順

- モデルの作成
- アソシエーション
- メソッド作成
- コントローラー
- ビュー

#モデルの作成

まずは、Relationshipモデルを作成していきます。

RelationshipモデルはUserモデルの`中間テーブル`にあたります。
なぜなら、フォローするのもフォローされるのもユーザーなので、`多 対 多`の状態になるからです。

カラムは、
`follower_id : フォローしたユーザー`
`followed_id : フォローされたユーザー`
を準備します。

ユーザーのidで、user_idにしていないのがミソです。
Userモデルの中間テーブルになるため、どちらもユーザーのidが入るので、わかりやすいように、あえてuser_idを使っていません。

説明もこれぐらいにして、コマンド実行していきます。

```
$ rails g model Relationship follower_id:integer followed_id:integer
$ rails db:migrate
```

これで、Relationshipモデルができました。

`follower_id : フォローしたユーザー`
`followed_id : フォローされたユーザー`
カラム名がややこしくて、よくわからなくなるので、注意してください。

#アソシエーション

中間テーブルは、この`アソシエーションの理解が１番難しい`です。
気合い入れてついてきてください！

まず、関係性を整理しておきます。

`１人のユーザーはたくさんのユーザーをフォローできる(1:N)`
`１人のユーザーはたくさんのユーザーにフォローされる(1:N)`

####relationshipモデル

１人のユーザーはたくさんのユーザーにフォローもできるし、フォローもされるので、`belongs_to`でいきましょう。

```models/relationship.rb
class Relationship < ApplicationRecord
  # class_name: "User"でUserモデルを参照
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"
end
```

`belongs_to :user`ちゃうの？
と思いませんか？

そうです。
本来、フォローしたユーザーとフォローされたユーザーは同じUserモデルから持ってきたいのですが、`belongs_to :user`とするとどっちがどっちのuserかわからなくなるので、`followerとfollowed`で分けています。

ただこのままだと、followerテーブルとfollowedテーブルを探しに行ってしまうので、`class_name: "User"`でuserテーブルからデータをとってきてもらうようにします。

####userモデル

では、続いてUser側のアソシエーションです。

```models/user.rb
# フォローをした、されたの関係
has_many :relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
has_many :reverse_of_relationships, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy
  
# 一覧画面で使う
has_many :followings, through: :relationships, source: :followed
has_many :followers, through: :reverse_of_relationships, source: :follower
```

まずは、フォローをした、されたの関係から見ていきます。
ここでは、relationshipsとreverse_of_relationshipsがありますが、さきほどと同じ考え方で、わかりにくいため名前をつけているだけです。
`class_name: "Relationship"`でRelationshipテーブルを参照します。

`foreign_key(外侮キー)`で参照するカラムを指定しています。

次に、フォロー・フォロワーの一覧画面で、`user.followers`という記述でフォロワーを表示したいので、`throughでスルーするテーブル`、`sourceで参照するカラム`を指定。

上の例では、reverse_of_relationshipsテーブルからfollower_idのデータを参照します。

#メソッド作成

モデルにメソッドを記述していきます。

```models/user.rb
# フォローしたときの処理
def follow(user_id)
  relationships.create(followed_id: user_id)
end
# フォローを外すときの処理
def unfollow(user_id)
  relationships.find_by(followed_id: user_id).destroy
end
# フォローしているか判定
def following?(user)
  followings.include?(user)
end
```

これで、コントローラーをすっきりできます。

#コントローラー

まずはrelationshipsコントローラを作成します。

```
$　rails g controller relationships followings followers
```

で、relationdhipsコントローラーに、createとdestroyアクションを追加します。

追加したらさきほどのメソッドを使って、コントローラーに記述していきます。

```controllers/relationships_controller.rb
class RelationshipsController < ApplicationController
  # フォローするとき
  def create
    current_user.follow(params[:user_id])
    redirect_to request.referer
  end
  # フォロー外すとき
  def destroy
    current_user.unfollow(params[:user_id])
    redirect_to request.referer  
  end
  # フォロー一覧
  def followings
    user = User.find(params[:user_id])
    @users = user.followings
  end
  # フォロワー一覧
  def followers
    user = User.find(params[:user_id])
    @users = user.followers
  end
end
```

####ルーティング

コントローラーを作成したので、ルーティングも設定しておきます。

```config/routes.rb
  # ネストさせる
  resources :users do
    resource :relationships, only: [:create, :destroy]
    get 'followings' => 'relationships#followings', as: 'followings'
    get 'followers' => 'relationships#followers', as: 'followers'
  end
```

#ビュー

ビューではフォローボタンとフォロー、フォロワー一覧画面を別々で見ていきます。

####フォローボタン

ビューのどこでもお好きなところに配置してください。

```erb:お好きなところ
<% if current_user.following?(user) %>
  <%= link_to "フォロー外す", user_relationships_path(user.id), method: :delete %>
<% else %>
  <%= link_to "フォローする", user_relationships_path(user.id), method: :post %>
<% end %>
```

####フォロー、フォロワー一覧画面

フォロー一覧もフォロワー一覧も同じ内容を表示するので、テンプレートをつくります。

```erb:views/relationships/_follow_list.html.erb
<% if users.exists? %>
  <% users.each do |user| %>
    <table>
      <thead>
        <tr>
          <th>name</th>
          <th></th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td><%= user.name %></td>
          <td>フォロー数: <%= user.followings.count %></td>
          <td>フォロワー数: <%= user.followers.count %></td>
        </tr>
      </tbody>
    </table>
  <% end %>
<% else %>
  <p>ユーザーはいません</p>
<% end %>
```

で、このテンプレートをrenderで呼び出してくれればOKです！

今回のビューはあくまで、一例なので適宜変更してください。

#まとめ

手順は以下

>- モデルの作成
- アソシエーション
- メソッド作成
- コントローラー
>- ビュー

中間テーブルの理解が初心者にはなかなかキツイものがありましたが、ぼくでもこうやって記事にできるぐらいにまとめられたので、みなさんもきっと大丈夫だと思います。

特にアソシエーションの部分が難しいので、理解できるまで見返してみてください。