LoginSignup
4

More than 3 years have passed since last update.

【Ruby on Rails】フォロー機能実装:双方向

Last updated at Posted at 2020-09-09

目標

follow.gif

開発環境

ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina

前提

※ ▶◯◯ を選択すると、説明等が出てきますので、
  よくわからない場合の参考にしていただければと思います。

一方向のブックマーク(お気に入り登録、いいね)機能はこちら

流れ

1 modelを作成
2 modelを修正 <-- ここが一番むずかしいです
3 controllerを作成
4 routingを修正
5 viewを作成

modelの作成

今回はRelationshipモデルを作成

ターミナル
$ rails g model Relationship follower_id:integer followed_id:integer
ターミナル
$ rails db:migrate

補足
follower_idとfollowed_idは架空のidであり、後ほどuserモデルの修正にて記述し、使用します。

modelを修正

app/models/relationship.rb
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"

補足
メソッド名を変更し、@relationship.followerのような形で、@relationshipに紐づいたuserレコードを取得。
※メソッド名変更は、フォローとフォロワーを分けるためです。

app/models/user.rb
  has_many :follower, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy # フォロー取得
  has_many :followed, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy # フォロワー取得
  has_many :following_user, through: :follower, source: :followed # 自分がフォローしている人
  has_many :follower_user, through: :followed, source: :follower # 自分をフォローしている人

  # ユーザーをフォローする、後ほどcontrollerで使用します。
  def follow(user_id)
    follower.create(followed_id: user_id)
  end

  # ユーザーのフォローを外す、後ほどcontrollerで使用します。
  def unfollow(user_id)
    follower.find_by(followed_id: user_id).destroy
  end

  # フォローしていればtrueを返す、後ほどviewで使用します。
  def following?(user)
    following_user.include?(user)
  end

補足1【1,2行目について】
<has_many :follower>
 まず、userは多くのfollowerを持ち、
<class_name: "Relationship">
 @userに紐づいた@relationshipレコードを取得可能にし、
<foreign_key: "follower_id">
 relaitonshipsテーブルにアクセスする時、follow_idを入口とし、
<dependent: :destroy>
 userがなくなれば削除する

補足2【3,4行目について】
<has_many :following_user>
 まず、userは多くのfollowing_userを持ち、
<through: :follower>
 followerを通してfollowing_userを取得可能にし、
<source: :followed>
 user.following_userで取得可能にする。

補足3【(followed_id: user_id)について】
(followed_id: user_id)は
followed_id に user_idを代入する
という風にイメージするとわかりやすいです。
あとは文字通りの意味です。

controllerを作成

ターミナル
$ rails g controller relationships
app/controllers/relationships.controller.rb
  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

今回はapp/views/homes/mypage.html.erbに
フォロー、フォロワーの一覧を表示するため下記の場所に記述。

app/controllers/homes.controller.rb
  def mypage
    @following_users = current_user.following_user
    @follower_users = current_user.follower_user
  end

routingを修正

config.routes.rb
resources :users do
  resource :relationships, only: [:create, :destroy]
end

viewに追加(インスタンス変数ありの場合)

app/views/show.html.erb
<% if current_user != user %>
  <% 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 %>
<% end %>
app/views/homes/mypage.html.erb
<div>
  フォロー数: <%= current_user.follower.count %>
  フォロワー数:<%= current_user.followed.count %>
</div><br><br>
<table>
    <caption>フォロー中</caption>
    <thead>
        <tr>
            <th>ユーザー名</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        <% @following_users.each do |following_user| %>
        <tr>
            <td><%= following_user.name %></td>
            <td><%= link_to 'フォロー外す', user_relationships_path(following_user.id), method: :delete %></td>
        </tr>
        <% end %>
    </tbody>
</table><br><br>

<table>
    <caption>フォロワー</caption>
    <thead>
        <tr>
            <th>フォロワー名</th>
        </tr>
    </thead>
    <tbody>
        <% @follower_users.each do |follower_user| %>
        <tr>
            <td><%= follower_user.name %></td>
        </tr>
        <% end %>
    </tbody>
</table>

参考

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
What you can do with signing up
4