記事概要
Ruby on Railsにフォロー機能を実装する方法について、まとめる
前提
- Ruby on Railsでアプリケーションを作成している
サンプルアプリ(GitHub)
フォロー機能
仕組み
フォローする側とフォローされる側のUserは多対多の関係になる
ER図
Userモデルは1つしか存在しないので、フォローする側とされる側のUserを区別できない
class_nameというオプションを使用することで、1つのモデルを便宜的に異なる2つのモデルとして考えられる。FollowingモデルとFollowerモデルの2つのモデルがあると仮定してER図を書く
手順1(必要なモデルを作成)
- Relationshipモデルを作成する
% rails g model relationship
- 必要なカラムを追加するため、マイグレーションファイルを編集する
db/migrate/20XXXXXXX_create_relationships.rb
class CreateRelationships < ActiveRecord::Migration[7.1] def change create_table :relationships do |t| t.references :following t.references :follower t.timestamps end end end
- 下記コマンドを実行する
% rails db:migrate
手順2(アソシエーションを設定)
- Relationshipモデルを編集する
app/model/relationship.rb
class Relationship < ApplicationRecord belongs_to :following, class_name: "User" # Userモデルに対して、2つのアソシエーションを組む belongs_to :follower, class_name: "User" # Userモデルに対して、2つのアソシエーションを組む end
- Userモデルを編集する
app/model/user.rb
# 省略 # フォローする側のUserから見た中間テーブルを「active_relationship」と再定義し、外部キー制約を設定 has_many :active_relationships, class_name: "Relationship", foreign_key: :following_id # throughオプションで中間テーブル(active_relationships)と紐付け、sourceオプションで参照先モデル(follower)を指定 has_many :followings, through: :active_relationships, source: :follower # フォローされる側のUserから見た中間テーブルを「passive_relationship」と再定義し、外部キー制約を設定 has_many :passive_relationships, class_name: "Relationship", foreign_key: :follower_id # throughオプションで中間テーブル(passive_relationships)と紐付け、sourceオプションで参照先モデル(following)を指定 has_many :followers, through: :passive_relationships, source: :following end
手順3(ルーティングを設定)
- ルーティングを設定する
config/routes.rb
# 省略 # usersとrelationshipsをネストする resources :users do resource :relationships, only: [:create, :destroy] end end
手順4(ビューを作成)
フォローの有無によって動的に表示を変える
- フォローしているかどうか判定するメソッドを作成する
app/model/user.rb
# 省略 # フォローしているかどうか判定するメソッド def followed_by?(user) # 中間テーブルを参照して、ユーザーidと一致するfollowing_idが存在すればその情報を取得。存在しなければnilを返す follower = passive_relationships.find_by(following_id: user.id) # followerに値が存在していればtrueを返し、存在していなければfalseを返す return follower.present? end end
- フォローボタンを作成するため、
app/views/users/show.html.erb
を編集する<div class="user-paged"> <div class="user-contentsd"> <p class="user-named">ユーザー名:<%= @user.name %></p> <%#= フォローボタン %> <% if @user.followed_by?(current_user) %> <%=link_to "フォロー中", user_relationships_path(@user.id),class: "btn", data: { turbo_method: :delete } %> <% else %> <%=link_to "フォローする", user_relationships_path(@user.id),class: "btn", data: { turbo_method: :post } %> <% end %> </div> </div>
手順5(コントローラーを作成)
- Relationshipコントローラーを作成する
% rails g controller relationships
- createアクションを定義する
app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController def create # ログイン中のユーザーに紐づいている中間テーブルの値を全て取得し、インスタンスを生成 follow = current_user.active_relationships.new(follower_id: params[:user_id]) # フォローした記録をDBに保存 follow.save # TOPページに移動 redirect_to root_path end end
-
current_user.active_relationships
と記述することで、ログイン中のユーザーに紐づいている中間テーブルの値を全て取得できる - newメソッドで、
current_user.active_relationships
のインスタンスを生成する -
(follower_id: params[:user_id])
の記述によって、生成する中間テーブルの値を指定する- 具体的には、中間テーブルのカラムである
follower_id
とparams
に格納されているuser_id
が一致するインスタンスを生成する
- 具体的には、中間テーブルのカラムである
-
- destroyアクションを定義する
app/controllers/relationships_controller.rb
# 省略 def destroy # ログイン中のユーザーと紐づいている中間テーブルの値を全て取得し、find_byメソッドを使用して該当する情報をDBから取得 follow = current_user.active_relationships.find_by(follower_id: params[:user_id]) # DBからフォローした記録を削除 follow.destroy # TOPページに移動 redirect_to root_path end end
手順6(ブラウザで動作確認)
手順7(コントローラーを編集)
- 自分自身をフォローできないようにするため、
app/views/users/show.html.erb
を編集する<div class="user-paged"> <div class="user-contentsd"> <p class="user-named">ユーザー名:<%= @user.name %></p> <%#= ログインユーザーと一致しない場合のみ、フォローボタンを表示 %> <% if current_user != @user %> <% if @user.followed_by?(current_user) %> <%=link_to "フォロー中", user_relationships_path(@user.id),class: "btn", data: { turbo_method: :delete } %> <% else %> <%=link_to "フォローする", user_relationships_path(@user.id),class: "btn", data: { turbo_method: :post } %> <% end %> <% end %> </div> </div>
- 自身のユーザー画面だとフォローボタンが表示されないことを、ブラウザで確認する
手順8(「フォロー中のユーザー」と「フォロワー」を表示)
- フォロワーを表示するため、
app/views/users/show.html.erb
を編集する<%#= 省略 %> <%#= フォロワーを表示 %> <h3>フォロワー</h3> <% @user.followers.each do |user| %> <p class=“f-user”><%= link_to user.name, user_path(user.id) %></p> <% end %> </div> </div>
-
@user.followers
は、@user
に格納されているユーザーのフォロワー情報を全て取得する
-
- フォロワーが表示されることを、ブラウザで確認する
- フォローしているユーザーを表示するため、
app/views/users/show.html.erb
を編集する<%#= 省略 %> <%#= フォローを表示 %> <h3>フォロー</h3> <% @user.followings.each do |user| %> <p class=“f-user”><%= link_to user.name, user_path(user.id) %></p> <% end %> <%#= フォロワーを表示 %> <h3>フォロワー</h3> <% @user.followers.each do |user| %> <p class=“f-user”><%= link_to user.name, user_path(user.id) %></p> <% end %> </div> </div>
-
@user.followings
は、@user
に格納されているユーザーのフォロー情報を全て取得する
-
- フォローが表示されることを、ブラウザで確認する