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番難しい
です。
気合い入れてついてきてください!
まず、関係性を整理しておきます。
1人のユーザーはたくさんのユーザーをフォローできる(1:N)
1人のユーザーはたくさんのユーザーにフォローされる(1:N)
####relationshipモデル
1人のユーザーはたくさんのユーザーにフォローもできるし、フォローもされるので、belongs_to
でいきましょう。
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側のアソシエーションです。
# フォローをした、されたの関係
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のデータを参照します。
#メソッド作成
モデルにメソッドを記述していきます。
# フォローしたときの処理
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アクションを追加します。
追加したらさきほどのメソッドを使って、コントローラーに記述していきます。
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
####ルーティング
コントローラーを作成したので、ルーティングも設定しておきます。
# ネストさせる
resources :users do
resource :relationships, only: [:create, :destroy]
get 'followings' => 'relationships#followings', as: 'followings'
get 'followers' => 'relationships#followers', as: 'followers'
end
#ビュー
ビューではフォローボタンとフォロー、フォロワー一覧画面を別々で見ていきます。
####フォローボタン
ビューのどこでもお好きなところに配置してください。
<% 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 %>
####フォロー、フォロワー一覧画面
フォロー一覧もフォロワー一覧も同じ内容を表示するので、テンプレートをつくります。
<% 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です!
今回のビューはあくまで、一例なので適宜変更してください。
#まとめ
手順は以下
- モデルの作成
- アソシエーション
- メソッド作成
- コントローラー
- ビュー
中間テーブルの理解が初心者にはなかなかキツイものがありましたが、ぼくでもこうやって記事にできるぐらいにまとめられたので、みなさんもきっと大丈夫だと思います。
特にアソシエーションの部分が難しいので、理解できるまで見返してみてください。