LoginSignup
19
42

More than 1 year has passed since last update.

[Rails]Ajaxを用いて非同期でフォロー機能の実装

Last updated at Posted at 2020-02-10

実装すること

Ajaxを用いて非同期通信を行い、フォロー機能を書いていきます。
Ajaxについては下記リンク先で説明しています。
[Rails]Ajaxを用いて非同期でコメント機能の実装:https://qiita.com/yuto_1014/items/c7d6213139a48833e21a

ER図

Userテーブル同士で「多対多」の関係を作ります。なぜなら、フォローしているユーザーもフォロワーもユーザになるからです。そこでUserテーブル同士の中間テーブルとしてRelationshipテーブルを作成します。
フォローER図.png

ページ設計

ユーザー詳細ページ(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度フォローしてしまわないようにするための一意の設定をしています。

20200109085109_create_relationships_.rb

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を削除します。

app/models/user.rb
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クラスであることを明示しています。

app/models/relationship.rb
    #自分をフォローしているユーザー
	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

config/routes.rb
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モデルのメソッドを呼び出しています。

app/controllers/users_controller.rb
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

フォローする・フォロー解除するためのアクションを作成します。
followunfollowでは、Userモデルのメソッドを呼び出しています。

app/controllers/relationships_controller.rb
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でフォローユーザーの人数を取っています。
・フォローボタンは、パーシャルにしています。

app/views/users/show.html

  <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モデルのメソッドを呼び出して確認しています。フォローしているか否かで呼び出されるパーシャルが変わってきます。

app/views/users/_follow_form.html
<!-- フォローボタン(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を代入しています。

app/views/users/_follow.html

<!-- フォローボタン ------------------------------------------------------------------>
<%= 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パーシャルを差し替えています。

app/views/relationships/create.js
$("#follow_form").html("<%= j(render("users/users/unfollow")) %>");

既にフォローしていた場合

users/_unfollow.html.erb
remote: trueで、relationshipsコントローラーのdestroyアクションに飛んだ後、destroy.js.erbに飛ぶようにしています。

app/views/users/_unfollow.html

<!-- フォロー解除ボタン ------------------------------------------------------------------>
<%= 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パーシャルを差し替えています。

app/views/relationships/destroy.js
$("#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

19
42
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
19
42