実装すること
Ajaxを用いて非同期通信を行い、フォロー機能を書いていきます。
Ajaxについては下記リンク先で説明しています。
[Rails]Ajaxを用いて非同期でコメント機能の実装:https://qiita.com/yuto_1014/items/c7d6213139a48833e21a
ER図
Userテーブル同士で「多対多」の関係を作ります。なぜなら、フォローしているユーザーもフォロワーもユーザになるからです。そこでUserテーブル同士の中間テーブルとしてRelationshipテーブルを作成します。
ページ設計
ユーザー詳細ページ(user/show.html.erb)でフォローができる。
モデルの作成
Userモデルは作成した前提で進めていきます。
作成手順は下記リンク先で説明しております。
[Rails]Ajaxを用いて非同期で投稿機能といいね機能の実装https://qiita.com/yuto_1014/items/78d8b52d33a12ec33448
それではRelationshipモデルを作成していきます。
$ rails g model Relationship follower_id:integer following_id:integer
$ rails db:migrate
マイグレーションファイルの追記
t.index [:follower_id, :following_id], unique: true
で、一度フォローしたユーザーを2度フォローしてしまわないようにするための一意の設定をしています。
class CreateRelationships < ActiveRecord::Migration[5.2]
def change
create_table :relationships do |t|
t.integer :follower_id
t.integer :following_id
t.index [:follower_id, :following_id], unique: true ←追記
t.timestamps
end
end
end
アソシエーションの確認
Userモデル
・『following_relationships』
following_relationshipsモデルを架空で作成しています。
class_name: "Relationship"
でRelationshipモデルの、foreign_key: "follower_id"
で、follower_idを参考に、following_relationshipsモデルへアクセスするようにしています。
フォロワーに関しても同じです。
・『following』
through: :following_relationships
で、中間テーブルにfollowing_relationshipsテーブルを指定しています。その結果、user.following と打つだけで、userが中間テーブルfollowing_relationships を取得し、その1つ1つのfollowing_idから、「フォローしているUser達」を取得できるようになります。
・『def following?』
following_relationshipsテーブルのfollowing_idにuserのidが存在するか確認しています。
・『def follow?』
フォローするときのメソッドを作成しています。
このメソッドが呼び出されたときには、following_idにuser.idを代入します。
・『def unfollow?』
フォローを外すときのメソッドを作成しています。
このメソッドが呼び出されたときには、following_idのuser.idを削除します。
class User < ApplicationRecord
has_many :following_relationships, foreign_key: "follower_id", class_name: "Relationship", dependent: :destroy
has_many :following, through: :following_relationships
has_many :follower_relationships, foreign_key: "following_id", class_name: "Relationship", dependent: :destroy
has_many :followers, through: :follower_relationships
#フォローしているかを確認するメソッド
def following?(user)
following_relationships.find_by(following_id: user.id)
end
#フォローするときのメソッド
def follow(user)
following_relationships.create!(following_id: user.id)
end
#フォローを外すときのメソッド
def unfollow(user)
following_relationships.find_by(following_id: user.id).destroy
end
Relationshipsモデル
class_name: "User"
と補足設定することで、followerクラス、followingクラスという存在しないクラスを参照することを防ぎ、Userクラスであることを明示しています。
#自分をフォローしているユーザー
belongs_to :follower, class_name: "User"
#自分がフォローしているユーザー
belongs_to :following, class_name: "User"
#バリデーション
validates :follower_id, presence: true
validates :following_id, presence: true
コントローラーの作成
$ rails g controller relationships
ルーティングの作成
フォローユーザーとフォロワーを取れるようにしています。
following_user GET /users/:id/following(.:format) users#following
followers_user GET /users/:id/followers(.:format) users#followers
Rails.application.routes.draw do
resources :users do
member do
get :following, :followers
end
end
resources :relationships, only: [:create, :destroy]
end
コントローラーの編集
users_controller.rb
フォローユーザー一覧とフォロワー一覧を表示するためのアクションを作成します。
@user.following
と@user.followers
では、Userモデルのメソッドを呼び出しています。
class UsersController < ApplicationController
def following
#@userがフォローしているユーザー
@user = User.find(params[:id])
@users = @user.following
render 'show_follow'
end
def followers
#@userをフォローしているユーザー
@user = User.find(params[:id])
@users = @user.followers
render 'show_follower'
end
end
ralationships_controller.rb
フォローする・フォロー解除するためのアクションを作成します。
follow
とunfollow
では、Userモデルのメソッドを呼び出しています。
class RelationshipsController < ApplicationController
def create
@user = User.find(params[:following_id])
current_user.follow(@user)
end
def destroy
@user = User.find(params[:id])
current_user.unfollow(@user)
end
end
ビューの編集
ユーザー詳細ページ(users/show.html.erb)
・followers.count
でフォロワーの人数、following.count
でフォローユーザーの人数を取っています。
・フォローボタンは、パーシャルにしています。
<div class="follower">
<%= link_to followers_users_user_path(user.id) do %>
<h5 style="color: black;">フォロワー<%= user.followers.count %>人</h5>
<% end %>
</div>
<div class="follow">
<%= link_to following_users_user_path(user.id) do %>
<h5 style="color: black;">フォロー<%= user.following.count %>人</h5>
<% end %>
</div>
<div>
<%= render "follow_form" %>
</div>
フォローボタン(users/_follow_form.html.erb)
・if user_signed_in? && @user != current_user
では、ユーザーがログインしていて且つユーザー詳細ページ(users/show.html.erb)に表示されているユーザーが、ログインユーザーでなければ、フォローボタンを表示するようにしています。
・if current_user.following?(@user)
で、ログインユーザーが@userをすでにフォローしているかどうかをUserモデルのメソッドを呼び出して確認しています。フォローしているか否かで呼び出されるパーシャルが変わってきます。
<!-- フォローボタン(MYPAGEのユーザーがcurrent_userでなければ表示------------------------------------------------------>
<% if user_signed_in? && @user != current_user %>
<div id="follow_form">
<% if current_user.following?(@user) %>
<%= render "unfollow" %>
<% else %>
<%= render "follow" %>
<% end %>
</div>
<% end %>
###まだフォローしていなかった場合
users/_follow.html.erb
・remote: true
で、relationshipsコントローラーのcreateアクションに飛んだ後、create.js.erbに飛ぶようにしています。
・hidden_field_tag :following_id, @user.id
で、following_idに@user.idを代入しています。
<!-- フォローボタン ------------------------------------------------------------------>
<%= form_for(current_user, url: relationships_path, method: :post, remote: true) do |f| %>
<%= hidden_field_tag :following_id, @user.id %>
<%= f.submit "フォローする", class: "btn btn-outline-secondary" %>
<% end %>
relationships/create.js.erb
relationshipsコントローラのcreateアクションの処理後、
users/_follow_form.htmlのid=follow_form
をターゲットに、unfollowパーシャルを差し替えています。
$("#follow_form").html("<%= j(render("users/users/unfollow")) %>");
既にフォローしていた場合
users/_unfollow.html.erb
remote: true
で、relationshipsコントローラーのdestroyアクションに飛んだ後、destroy.js.erbに飛ぶようにしています。
<!-- フォロー解除ボタン ------------------------------------------------------------------>
<%= form_for(current_user, url: relationship_path(@user), method: :delete, remote: true) do |f| %>
<%= f.submit "フォロー解除", class: "btn btn-outline-secondary" %>
<% end %>
relationships/destroy.js.erb
relationshipsコントローラのdestroyアクションの処理後、
users/_follow_form.htmlのid=follow_form
をターゲットに、followパーシャルを差し替えています。
$("#follow_form").html("<%= j(render("users/users/follow")) %>");
最後に
最後までご覧いただきありがとうございます。
初学者ですので間違っていたり、分かりづらい部分もあるかと思います。
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。
Ajax関連でいいねとコメントの非同期も実装しています。
↓
[Rails]Ajaxを用いて非同期で投稿機能といいね機能の実装
https://qiita.com/yuto_1014/items/78d8b52d33a12ec33448
[Rails]Ajaxを用いて非同期でコメント機能の実装
https://qiita.com/yuto_1014/items/c7d6213139a48833e21a
参考
・railsでフォロー機能をつける。
https://qiita.com/kitaokeita/items/59b625e0c43a62f5fe6a
・Railsでフォロー機能を作る方法
https://qiita.com/MitsuguSueyoshi/items/e41e2ff37f143db81897
・フォロー機能、完成版
https://qiita.com/Kaisyou/items/86869db6345c9cc1413f