目的
Railsで作成したアプリに非同期でのフォロー機能を導入する。
開発環境
macOS: Big Sur
Rubyバージョン: 2.6.5
Railsバージョン: 6.0.0
前提
- アプリ
test-app
が作成されている。
【Rails】簡単な投稿アプリの作成 -
devise
が導入されている。
【Rails】ユーザー管理機能(devise)の導入 -
jQuery
が導入されている。
【Rails】iQueryの導入
手順
- はじめに
- ルーティングの設定
- Relationshipモデルの作成
- アソシエーション設定
- バリデーションの設定
- メソッドの定義
- relationshipsコントローラーの作成
- アクションの定義
- フォローボタンの表示
- jsファイルの作成
はじめに
今回はフォロー機能を実装していきます。
フォロー機能に必要なテーブルは中間テーブルであるrelationships
ですが、
フォローするuser
とフォローされるuser
は同じなのでアソシエーションが通常の「多対多」とは異なります。
ルーティングの設定
それでは早速始めていきます!
まずはルーティングの設定です。
Rails.application.routes.draw do
#省略
resources :relationships, only: [:create, :destroy]
end
機能としてはフォロー
とフォロー解除
のみなので、create
とdestroy
のみを設定します。
Relationshipモデルの作成
% rails g model relationship
続いてテーブルを作成していきます。
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_id
はusersテーブル
を参照しなければならないため{ to_table: :users }
という記述でテーブルを指定しています。
t.index [:user_id, :follower_id], unique: true
はuser_id
とfollower_id
のペアが重複しないための記述です。
ある1人のユーザーが、あるユーザーを何度もフォローできないようにしています。
% rails db:migrate
マイグレーションも忘れずに!
アソシエーションの設定
class Relationship < ApplicationRecord
belongs_to :user
belongs_to :follower, class_name: 'User'
end
class_name: 'User'
の記述で、Userモデル
を参照するように指示しています。
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
を取得できます。
バリデーションの設定
class Relationship < ApplicationRecord
#省略
with_options presence: true do
validates :user_id
validates :follower_id
end
end
user_id
とfollow_id
は必須というバリデーションを追加します。
メソッドの定義
後ほど使用するメソッドを定義します。
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アクション
を定義します。
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
先ほど定義したメソッドはここで使用します。
フォローボタンの表示
フォローボタンの表示は部分テンプレートとして切り分け、ユーザー詳細ページに部分テンプレートを呼び出す記述をします。
<div id = "relationship_<%= @user.id %>">
<%= render partial: "relationships/relationship", locals: { user: @user } %>
</div>
部分テンプレートとして切り分けたページです。
<% 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ファイルを作成します。
$('#relationship_<%= @user.id %>').html("<%= j(render partial: 'relationships/relationship', locals: { user: @user }) %>");
$('#relationship_<%= @user.id %>').html("<%= j(render partial: 'relationships/relationship', locals: { user: @user }) %>");
これで非同期でのフォロー機能が実装できたと思います。
確認してみてください!
最後に
以上でフォロー機能の実装は完了です。
CSSは実装していないため、簡素な見た目となっています。各自必要に応じて編集してください。
では。