フォロー機能を実装しよう
自分のWEBアプリケーションにフォロー機能を
付けたい人多いのではないでしょうか?
ただ、フォロー機能は中間テーブルを用いて多対多を行っているため
初めての人にとっては分かりづらいのも事実。
特にアソシエーションの部分についてを詳しく説明していくので
初学者でも分かりやすく理解できるよう解説していきます。
多対多とは?
まず実装する前に多対多についてを説明していこうと思います!
この概念を理解していないと???が浮かんでくるので
しっかりとおさえてくださいませ!
通常であれば1対多、多対1のような状態であることが多いです。
例) 1対多(投稿機能)
ー--------------ー-----
・ユーザー >> 投稿 = 1対多の関係です。
1人のユーザーに投稿はたくさんつきます。
・投稿 >> ユーザー = 多対1の関係です
その一方一つの投稿は一人のユーザーに決まります。
ー--------------ー-----
まずここまで理解できますでしょうか?
次に多対多の関係を見ていきます。
・
・
・
例) 多対多(フォロー機能)
ー--------------ー-----
・ユーザーA >> ユーザーB
ユーザーc
ユーザーd
ユーザーAはたくさんの人をフォローしてます。
ー--------------ー-----
・ユーザーB >> ユーザーA
ユーザーC
ユーザーd
ユーザーBはたくさんの人をフォローしています。
ー--------------ー-----
このように片方が1対多にならない関係を
多対多といいます。
フォロー機能がその良い例ですね!
ここまで簡単に多対多について説明をしました。
それでは次から実装にはいっていきましょう!
実装
⓵ マイグレーションファイル
⓶ モデル
➂ ルーティング
⓸ コントローラー
⓹ ビューファイル
それでは、いってみよー!
ステップ➀:マイグレーションファイル
一番名グレーションファイルからコードを見ていきましょう!
まずはコードをお見せしますね♪
class CreateFavorites < ActiveRecord::Migration[5.2]
def change
create_table :favorites do |t|
t.integer :follow_id, null: false
t.integer :follower_id, null: false
t.timestamps
end
end
end
※ userテーブルは既に作成済みとして進みます。
ちなみにnull falseというのは空欄である場合に
登録をさせないようにするための記述となりますよ。
空欄で保存させないためにも記載をしておきましょう!
できたらrails db migrateを実行です!
ステップ➁:モデルファイル
次にモデルファイルにコードを記載していきます。
これが少し難しいので理解をしていく必要があります。
class User < ApplicationRecord
#フォロー機能のアソシエーション
#follower_id=自分
#follow_id=相手
上パート
----------------
#自分がフォローしたり、アンフォローしたりするための記述
➀has_many :favorites, class_name: "Favorite", foreign_key: "follower_id", dependent: :destroy
#sourceは本当はfollow_idとなっていてカラム名を示している。
#フォロー一覧を表示するための記述
⓶has_many :followings, through: :favorites, source: :follow
----------------
下パート
----------------
⓷has_many :reverse_favorites, class_name: "Favorite", foreign_key: "follow_id", dependent: :destroy
#フォロワー一覧を表示するための記述
⓸has_many :followers, through: :reverse_favorites, source: :follower
----------------
分かりやすくするために点線と上パートと下パートと記述をしました。
二つのパートを用意してはいますが、フォローとアンフォローだけであれば
上パートの➀だけあれば実はフォローとアンフォローの機能はできてしまいます。
しかしなぜ、⓶⓷⓸はあるの?というお話になると思いますが
これはフォローとフォロワーの一覧を表示するために必要な機能なんですね。
フォローとアンフォローだけであれば➀だけつくればいいですが
フォロー機能をつくったらフォロー数とフォロワー数や一覧なども
みたくなりますよね?
なので、⓶⓷⓸を記述しているわけです。
さて、本題にもどりますが、これらがどんな役割かというと
➀has_many :favorites, class_name: "Favorite", foreign_key: "follower_id", dependent: :destroy
⓷has_many :reverse_favorites, class_name: "Favorite", foreign_key: "follow_id", dependent: :destroy
➀と➂の最初のfavoritesやreverse_favoritesというのは
仮の名前をつけているわけなんですね。
なぜかというと、1対多であればhas_manyというのは、
上パートと下パートと分ける必要がないので、
一つのカラムに指定をすればいいのですが
多対多という場合は中間テーブルというのを用いて
無理やり?テーブル同士の繋がりを実現することになります。
つまり疑似的にuserテーブルを二つあるかのような設定を作り上げます。
一覧表示を表示する際は、
自分がフォローしている人の一覧を表示するためと
自分のことをフォローしてくれている人の一覧を表示するために
上パートと下パートを用意することでそれを実現できます。
しかし現実的にはfavoriteテーブルというのは
一つしかないので、仮の名前を1つ目で作成しているわけなんですね。
次にclass_nameというのは、これが本当のテーブルの名前です。
foreign_keyというのは、外部キー指定というもので
このカラムに保存してくださいとアソシエーションで指定できます。
dependent destroyというのは、
userが削除されたらそれに紐づいているアソシエーションもすべて削除されます。
次にこちらの解説をします。
これらは一覧表示をするために必要になります。
⓶has_many :followings, through: :favorites, source: :follow
⓷has_many :reverse_favorites, class_name: "Favorite", foreign_key: "follow_id", dependent: :destroy
ここでのポイントはthroughとsourceという記載があることです。
throughはfavoritesテーブルを通してという意味になります。
sourceというのは実際のカラムになります。
sourceであればfollowカラムを参照するということなので
favoritesテーブルを通じて自分がフォローしている人すべての人を取得できます。
ちなみにですが、sourceは出口と覚えていただくと分かりやすいと思います。
➀has_many :favorites, class_name: "Favorite", foreign_key: "follower_id", dependent: :destroy
⓶has_many :followings, through: :favorites, source: :follow
この例で行くと、foreign_keyがfollower_idで入口で、source: :followが出口です。
従いまして、自分がフォローしている人一覧を表示する際は
まず自分のidなので、follower_idを探してきて、それに紐づくsourceのfollowキーを
railsが引っ張ってくれるわけですね。
自分のことをフォローしてくれている人を表示したい時は
逆の場合も同じように考えればいいわけです!
class Favorite < ApplicationRecord
# userへのアソシエーション
# class_nameがuserモデルとリンクしている(class_nameは大文字でなければNG)
belongs_to :follow, class_name: "User"
belongs_to :follower, class_name: "User"
1対多であれば、上記の場合であれば
belongs_to: userという記述になりますが
favoriteモデルの場合は多対多になるため
belongs_toが二つ必要になります。
なぜかというと、userモデルでsourceを指定しているので
書かないと繋がりをつくれなくなってしまうからですね。
class_nameはユーザーモデルを示しています。
さて、フォロー機能で一番難しいといっても過言ではない
モデル間のアソシエーション機能については完了となります。
次はルーティングをみていくとしましょう。
ステップ⓷:ルーティングファイル
それでは、ステップ⓷はルーティングファイルを記載していきます。
Rails.application.routes.draw do
# users用のURL用に設定
resources :users, only: [:edit, :update] do
# フォロー機能はuserにネストさせている
resource :favorites, only: [:create, :destroy]
get 'followings' => 'favorites#followings', as: 'followings'
get 'followers' => 'favorites#followers', as: 'followers'
end
resource :favorites, only: [:create, :destroy]は、
フォローとアンフォローをするだけあればOKなのでcreateとdestroyだけを記述しています。
そしてその下は、一覧表示するための記述です。
get 'followings' => 'favorites#followings', as: 'followings'
get 'followers' => 'favorites#followers', as: 'followers'
user_idに基づいて、フォローしたりアンフォローしたりするので
user_idネストをすることでurlにidを含めることができます。
ステップ⓸:コントローラー
さて、マイグレーションファイル、モデル、ルーティングが完了したら
次はコントローラーを記述していきます。
class FavoritesController < ApplicationController
# フォローアンフォロー処理
def create
# params[:user_id]これはリンクから送られてきたuser_idをparamsで受け取っている
# そして受け取った値をモデルのメソッドに受け渡している
@user = User.find(params[:user_id])
current_user.follow(params[:user_id])
#フォローの通知機能
@user.create_notification_follow!(current_user)
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
まずは、フォローアンフォローの機能からみていきましょう。
def create
current_user.follow(params[:user_id])
end
def destroy
current_user.unfollow(params[:user_id])
redirect_to request.referer
end
フォロー機能はこれだけでできます。
crateとdestroyで何をやっているかというと
current_userでfollowメソッドを走らせてと行っています。
そしてfollowにはuser_idをひきわたしてあげます。
このfollowというのは何かというと
モデルに自作することができるメソッドのことです。
モデルにメソッドを書くことでコントローラー内の
記述がスッキリするのでモデルにメソッドをかけるのであれば
モデルにメソッドを記述していきましょう!
モデル内に書いたメソッドはこれです。
class User < ApplicationRecord
def follow(user_id)
favorites.create(follow_id: user_id)
end
def unfollow(user_id)
favorites.find_by(follow_id: user_id).destroy
end
さて、コントローラーの記述に戻りますが
下記のfollowというのと、モデルのfollowが一致していますね。
current_user.follow(params[:user_id])
def follow(user_id)
favorites.create(follow_id: user_id)
end
つまり、コントローラーのfollowはモデルの自作したfollowメソッドを
引っ張ってきているわけなんですね。
通常であればコントローラーに.updateや.createと
記載をすると思うのですがモデルにモデルにかけるので
モデルに.createを記載していきます。
コントローラーでcurrent_userという記述をしているので
現在ログインしているユーザーの情報をもったまま
モデルに書いたメソッドにログインユーザーの情報を引き渡して
モデルに書いた処理を実行することができます。
フォローボタンを押した際にフォローした相手のuser_idが
follow_idカラムに保存され、follower_idにはcurrent_userのidが
自動で入るようにrailsがおこなってくれるわけですね。
favorites.create(follow_id: user_id)
逆も同じような考えて削除するときも同じように削除を行います。
# フォローフォロワー一覧処理
def followings
user = User.find(params[:user_id])
@users = user.followings
end
def followers
user = User.find(params[:user_id])
@users = user.followers
end
フォロー一覧をする際にはUser.findをまずして
誰がuser_idなのかを突き止めてから
user.followingsでアソシエーションをして
user_idがフォローしている人一覧を取得します。
これにてコントローラーの解説は完了となります。
最後はviewファイルをみていきますよ!
ステップ⑤:viewファイル
最後はいよいよビューファイルをみていきます!
まずは、フォロー機能とフォロー一覧機能で
分けてみていきましょう!
<% @friends.each do |friend| %>
<tr>
<td class="text-center"><%= link_to friend_posts_path(friend) do %>
<%= attachment_image_tag friend, :profile_image, fallback: "no_image.jpg", size: '70x70', class:"mt-3 rounded-circle"; %>
<% end %>
</td>
<td class="text-center align-middle"><%= friend.nickname %></td>
<td class="text-center align-middle"><%= friend.height %></td>
<td class="text-center align-middle"><%= friend.weight %></td>
<td class="text-center align-middle"><%= friend.introduction %></td>
<!--ログインユーザーでない場合にフォローボタン表示-->
<% if friend != current_user %>
<td class="text-center align-middle"><% if current_user.following?(friend) %>
<%= link_to "Unfollow", user_favorites_path(friend), method: :delete %>
<% else %>
<%= link_to "Follow", user_favorites_path(friend), method: :post %>
<% end %>
</td>
<% end %>
</td>
</tr>
<% end %>
eachの@friendsは、userコントローラー内のfriendsコントローラーで
index(または自作)アクション内に指定をしたインスタンス変数を指定してあげます。
def friends
@friends = User.all
end
eachではUserの情報が入っているのでブロック変数のfriendを
link_toで当ててあげましょう。
<% if friend != current_user %>
<%= link_to "Unfollow", user_favorites_path(friend), method: :delete %>
<% else %>
<%= link_to "Follow", user_favorites_path(friend), method: :post %>
<% end %>
フォローアンフォロー機能を作成するには、link_toを記載して
methodでdeleteかpostを指定してあげることで、
favoritesコントローラーのfollow、unfollowアクションを
走らせることができます。
そして、もう一つの肝となるのが、
フォロー機能を判定するメソッドです。
<% if current_user.following?(friend) %>
これはuserモデルにこのメソッドを記載しています。
followingsは、アソシエーションになるので
followingsはfollowカラムを参照するので
自分がフォローしているユーザーを探すことができます。
(user)の引数には相手のidが入ってくるため
<% if current_user.following?(friend) %>
でcurrent_userと指定していることで
自分のidがフォローしているかincludeで判定をします。
# 引数そのものを検索する
# フォローを判定しているメソッド
def following?(user)
followings.include?(user)
end
自分が相手をフォロしていればunfollowを表示できるようになり
そうでなければfollowと表示をviewの分岐で行います。
これにて、フォロー機能は完成です!
まとめ
いかがでしたでしょうか?
フォロー機能は少し複雑にはなりますが
ポイントはアソシエーションの理解になります。
ここさえ掴めれば自分でも理解に落とし込めるはずです。
何度も復習してみてくださいね!
それでは、ちゃお!