LoginSignup
0
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>

参考

0
4
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
0
4