2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails】フォロー機能の実装(非同期)

Last updated at Posted at 2021-06-06

目的

Railsで作成したアプリに非同期でのフォロー機能を導入する。

開発環境

macOS: Big Sur
Rubyバージョン: 2.6.5
Railsバージョン: 6.0.0

前提

手順

  1. はじめに
  2. ルーティングの設定
  3. Relationshipモデルの作成
  4. アソシエーション設定
  5. バリデーションの設定
  6. メソッドの定義
  7. relationshipsコントローラーの作成
  8. アクションの定義
  9. フォローボタンの表示
  10. jsファイルの作成

はじめに

今回はフォロー機能を実装していきます。
フォロー機能に必要なテーブルは中間テーブルであるrelationshipsですが、
フォローするuserフォローされるuserは同じなのでアソシエーションが通常の「多対多」とは異なります。

ルーティングの設定

それでは早速始めていきます!
まずはルーティングの設定です。

config/routes.rb
Rails.application.routes.draw do
#省略
  resources :relationships, only: [:create, :destroy]
end

機能としてはフォローフォロー解除のみなので、createdestroyのみを設定します。

Relationshipモデルの作成

ターミナル
% rails g model relationship

続いてテーブルを作成していきます。

db/migrate/20XXXXXXXXXXXX_create_relationships.rb
class CreateRelationships < ActiveRecord::Migration[5.0]
  def change
    create_table :relationships do |t|
      t.references :user, foreign_key: true
      t.references :follower, foreign_key: { to_table: :users }
      t.timestamps

      t.index [:user_id, :follower_id], unique: true
    end
  end
end

t.references :follower, foreign_key: { to_table: :users }の記述ですが、
follower_idusersテーブルを参照しなければならないため{ to_table: :users }という記述でテーブルを指定しています。

t.index [:user_id, :follower_id], unique: trueuser_idfollower_idのペアが重複しないための記述です。
ある1人のユーザーが、あるユーザーを何度もフォローできないようにしています。

ターミナル
% rails db:migrate

マイグレーションも忘れずに!

アソシエーションの設定

app/models/relationship.rb
class Relationship < ApplicationRecord
  belongs_to :user
  belongs_to :follower, class_name: 'User'
end

class_name: 'User'の記述で、Userモデルを参照するように指示しています。

app/models/user.rb
class User < ApplicationRecord
  has_many :relationships, dependent: :destroy
  has_many :followings, through: :relationships, source: :follower
  has_many :passive_relationships, class_name: 'Relationship', foreign_key: 'follower_id', dependent: :destroy
  has_many :followers, through: :passive_relationships, source: :user
end

followings, through: :relationships, source: :followerの記述は、
relationshipsテーブルfollower_idを参考にして、followingsモデルにアクセスするという意味です。
結果として、user.followingsと記述するだけで、フォローしているUserを取得できます。

has_many :followers, through: :passive_relationships, source: :userの記述も同様で、user.followersと記述するだけでm自分をフォローしているUserを取得できます。

バリデーションの設定

app/models/relationship.rb
class Relationship < ApplicationRecord
#省略
  with_options presence: true do
    validates :user_id
    validates :follower_id
  end
end

user_idfollow_idは必須というバリデーションを追加します。

メソッドの定義

後ほど使用するメソッドを定義します。

app/models/user.rb
class User < ApplicationRecord
#省略
  def following?(user)
    followings.include?(user)
  end

  def follow(user_id)
    relationships.create(follower: user_id)
  end

  def unfollow(relationship_id)
    relationships.find(relationship_id).destroy!
  end
end

following?は既にフォローしているか?という記述、followはフォローを保存する記述、unfollowはフォロー解除する記述です。

relationshipsコントローラーの作成

ターミナル
% rails g controller relationships

アクションの定義

フォローフォロー解除機能のため、createアクションdestroyアクションを定義します。

app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController
  before_action :authenticate_user!

  def create
    @user = User.find(params[:follower])
    current_user.follow(@user)
  end

  def destroy
    @user = current_user.relationships.find(params[:id]).follower
    current_user.unfollow(params[:id])
  end
end

先ほど定義したメソッドはここで使用します。

フォローボタンの表示

フォローボタンの表示は部分テンプレートとして切り分け、ユーザー詳細ページに部分テンプレートを呼び出す記述をします。

app/views/users/show.html.erb
<div id = "relationship_<%= @user.id %>">
  <%= render partial: "relationships/relationship", locals: { user: @user } %>
</div>

部分テンプレートとして切り分けたページです。

app/views/users/_relationship.html.erb
<% if user_signed_in? && current_user.id != @user.id %>
  <% if current_user.following?(@user) %>
    <%= button_to "フォロー解除", relationship_path(current_user.relationships.find_by(follower: @user)), method: :delete, remote: true %>
  <% else %>
    <%= button_to "フォロー", relationships_path(follower: @user), method: :post, remote: true %>
  <% end %>
<% end %>

サインインしているか?既にフォローしていないか?という条件でフォローボタンフォロー解除ボタンを条件分岐しています。

jsファイルの作成

フォロー機能を非同期で行うためのjsファイルを作成します。

app/views/relationships/create.js.erb
$('#relationship_<%= @user.id %>').html("<%= j(render partial: 'relationships/relationship', locals: { user: @user }) %>");
app/views/relationships/destroy.js.erb
$('#relationship_<%= @user.id %>').html("<%= j(render partial: 'relationships/relationship', locals: { user: @user }) %>");

これで非同期でのフォロー機能が実装できたと思います。
確認してみてください!

最後に

以上でフォロー機能の実装は完了です。
CSSは実装していないため、簡素な見た目となっています。各自必要に応じて編集してください。
では。

2
2
1

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
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?