🌷

Rails フォロー機能

2023/04/28に公開

フォロー・フォロワー機能の追加

実装する機能

コントローラ

  • relationshipsコントローラを追加
  • createアクションを追加(用途:フォローを作成)
  • destroyアクションを追加(用途:フォローを削除)
  • フォローする・外すボタンをクリックしたら元画面に遷移すること

モデル

  • relationshipモデルを作成

ビュー

  • サイドバーにフォロー数・フォロワー数を表示
  • マイページ以外のサイドバーにフォローする・外すボタンを追加
  • ユーザー一覧画面にフォロー数・フォロワー数・フォローする・外すボタンの設置
  • フォロー・フォロワー一覧画面を作成すること

手順

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

モデルの作成

フォロー機能の場合、フォローするのもされるのもユーザであるため、
少なくとも片方は、user_id以外のカラム名にする必要がある。

relationshipsテーブルに、以下カラムを作成!

カラム名 データ型 説明
id (初期カラム) 主キー (PK)
follower_id integer フォローするユーザのid
followed_id integer フォローされるユーザのid

普通、外部キーとテーブル名は一致していないといけないので
各テーブルをfollowerテーブルとfollowerdテーブルにする。

rails g model Relationship follower_id:integer followed_id:integer

rails db:migrateも忘れずに!

アソシエーション

models/relationship.rb

class Relationship < ApplicationRecord
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"
end

belongs_to :userとすると、どっちがどっちのuserかわからなくなるので、followerとfollowedで分けている。

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

class_nameとは
関連名と参照先のクラス名を異なるものに置き換えることができるオプション。
モデル名を直接指定できる。
関連付けの相手となるオブジェクト名を関連付け名から生成できない事情がある場合に役立つ!

models/user.rb

class User < ApplicationRecord

# フォローをした、されたの関係
has_many :followers, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
has_many :followeds, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy

# 一覧画面で使う
has_many :following_users, through: :followers, source: :followed
has_many :follower_users, through: :followeds, source: :follower

end

まずは、フォローをした、されたの関係から!
先ほどと同じ考えでfollower、followedと名前をつけている。
class_name: "Relationship"でRelationshipテーブルを参照。
foreign_keyで参照するカラムを指定している。

次に、フォロー・フォロワーの一覧画面(foreign_keyで繋いだところの定義)

following_user:中間テーブルを通し、followerモデルのフォローされる側を取得
follower_user:中間テーブルを通し、followedモデルのフォローする側を取得

メソッド作成

モデルにメソッドを記述していく。
これで、コントローラーをすっきりできる🙌🏻

# フォローしたときの処理
def follow(user_id)
  followers.create(followed_id: user_id)
end

# フォローを外すときの処理
def unfollow(user_id)
  followers.find_by(followed_id: user_id).destroy
end

#フォローしていればtrueを返す
def following?(user)
  following_users.include?(user)
end	 

ルーティングの設定

Userと、Relationshipは関連づけられているためuserのidが必要。
relationshipsをネストする。

resources :users, only: [:index, :show, :edit, :update] do
    member do
      get :follows, :followers
    end
      resource :relationships, only: [:create, :destroy]
  end

userのidを含んだ形で、個々のリソースに対してfollowsアクションとfollowersアクションが設定できた!

memberとは

resourcesで生成されるルートに、決められたルート以外のルートを追加するための処理!
resourcesのブロック(doとend)の中で使います。

ルートを生成したときに:id をつけたくない場合はcollectionメソッドを使う。
https://prograshi.com/framework/rails/collection-and-member/

コントローラー

rails g controller relationships

relationships_controller

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
end

users_controller

# フォロー一覧
def follows
  user = User.find(params[:id])
  @users = user.following_users
end

# フォロワー一覧
def followers
  user = User.find(params[:id])
  @user = user.follower_users
end

ユーザー詳細ページで、フォロー数とフォロワー数を表示するための記述

 def show
    @user = User.find(params[:id])
    @following_users = @user.following_users
    @follower_users = @user.follower_users
  end

ビュー

まずこの部分を作成!

user_infoの部分テンプレートに追記しました!

<th>Follows</th>
      <td>
        <%= link_to follows_user_path(user) do %>
          <h6><%= user.followers.count %></h6>
        <% end %> 
      </td>
      </tr>
      <tr>  
        <th>Followers</th>
        <td>  
          <%= link_to followers_user_path(user) do %>
            <h6><%= user.followeds.count %></h6>
          <% end %>
        </td>
      </tr> 
      <tr>
      <td>
      <% if current_user != user %>
          <% if current_user.following?(user) %>
            <%= link_to "フォロー外す", user_relationships_path(user.id), method: :delete, class: "btn btn-danger" %>
          <% else %>
            <%= link_to "フォローする", user_relationships_path(user.id), method: :post, class:"btn btn-success" %>
          <% end %>
        <% end %>
      </td>

render部分

<%= render partial: "partial/user_info", locals: { user: current_user }%>

<% if current_user != user %>
この記述で、ログインしているユーザー自身にはフォローボタンが表示されないようにしている。

次にこの部分!

フォロー、フォロワー数を載せたいところに記述!
users/index.html.erb

<td>フォロー<%= user.followers.count %></td>
<td>フォロワー<%= user.followeds.count %></td>

フォローボタンも追加!(部分テンプレートを使用)
renderでcurrent_userを渡した。

<td><%= render partial: "relationships/follow_btn", locals: { user: current_user } %></td>

relationships/_follow_btn

<% if current_user.following?(user) %>
  <%= link_to "フォロー外す", user_relationships_path(user.id), method: :delete, class: "btn btn-danger" %>
<% else %>
  <%= link_to "フォローする", user_relationships_path(user.id), method: :post, class:"btn btn-success" %>
<% end %>

次にフォロー・フォロワー一覧画面も作成!

view/users配下にfollows.html.erbとfollowers.html.erbを作成!

follows.html.erb

<h2>Follows</h2>
  <table class='table'>
    <thead>
      <tr>
        <th>Image</th>
        <th>name</th>
        <th></th>
        <th></th>
      </tr>
    </thead>
    <tbody>
      <% @users.each do |user| %>
        <tr>
          <td>
            <%= link_to user_path(user) do %>
              <%= image_tag user.get_profile_image(50,50) %>
            <% end %>
          </td>
          <td><%= user.name %></td>
          <td>
            <%= link_to follows_user_path(user) do %>
              <h6>フォロー<%= user.followers.count %></h6>
            <% end %>
          </td>
          <td>  
            <%= link_to followers_user_path(user) do %>
              <h6>フォロワー<%= user.followeds.count %></h6>
            <% end %>
          </td>
        </tr>
      <% end %>
    </tbody>
  </table> 

followers.html.erb

<h2>Followers</h2>
<table class='table'>
  <thead>
    <tr>
      <th>Image</th>
      <th>name</th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <% @user.each do |user| %>
      <tr>
        <td>
          <%= link_to user_path(user) do %>
            <%= image_tag user.get_profile_image(50,50) %>
          <% end %>
        </td>
        <td><%= user.name %></td>
        <td>
          <%= link_to follows_user_path(user) do %>
            <p>フォロー<%= user.followers.count %></p>
          <% end %>
        </td>
        <td>  
          <%= link_to followers_user_path(user) do %>
            <p>フォロワー<%= user.followeds.count %></p>
          <% end %>
        </td>
      </tr>
    <% end %>
  </tbody>
</table> 

users_controllerのこの部分!
eachに入れるインスタンス変数をそれぞれ入れる。

 def follows
   user = User.find(params[:id])
   @users = user.following_users
 end

 def followers
   user = User.find(params[:id])
   @user = user.follower_users
 end

完成!

参照
https://qiita.com/nakachan1994/items/e6107fe3003f6515e385

https://zenn.dev/airiswim/articles/f854a095a3f3b8

復習で2回目の実装!
前より理解しながら進められた🙆🏻‍♀️

Discussion