LoginSignup
61
62

More than 1 year has passed since last update.

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

Posted at

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でいきましょう。

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

ビュー

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

フォローボタン

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

お好きなところ
<% 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 %>

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

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

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です!

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

まとめ

手順は以下

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

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

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

61
62
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
61
62