何をしたか
この記事は、こちらの記事の続きです。
▼前回の記事
Railsでユーザーフォロー機能を実装する(Ajax使うよ)①
Railsの課題を実施しています。User
のフォロー機能を実装することになりました。
前回までの記事では、マイグレーションファイルの作成と、モデルファイルへのアソシエーションの定義が終わったので、今回はcontroller
とview
、そしてモデルメソッドを追加していきます。
なお、実行環境は以下の通りです。
Rails 5.2.3
Ruby 2.6.0
ゴール
今回のゴールはこんな感じです。ボタンを押すとサクサクと非同期でフォロー/フォロー解除のボタンが現れます。
仕様
この機能の使用は以下の通りです。
- ユーザーは他のユーザーをフォローできる
- 同じユーザーを2回フォローはできない
- ユーザーは自分をフォローできない
データ構造
なぜこうなるのかは、前回の記事を読んでくださいね
非同期ではない実装
まずは、非同期ではない形で実装していきます。
controller
、model
ともにさらっと書いていきますので、詳しく知りたい方はこの辺の記事をご覧になると良いかもしれません。
- Railsで「いいね!」機能を作る - ②「いいね!」のcreateアクション
- Railsで「いいね!」機能を作る - ③「いいね!」を解除できるようにする
- RailsでAjaxで「いいね!」機能を実装する。
controller
controller
の記載は以下の通りです。
class RelationshipsController < ApplicationController
def create
@other_user = User.find(params[:follower])
current_user.follow(@other_user)
end
def destroy
@user = current_user.relationships.find(params[:id]).follower
current_user.unfollow(params[:id])
end
end
follow
、unfollow
はモデルメソッドです。後ほど解説します。
view
ビューファイルの記述は以下の通りです。なお、装飾のためのクラスは省いています。なお、user
(それぞれのユーザー)を表す変数がここに表示しているコードの外から渡っているものとします。
- if logged_in? && current_user != user
- if current_user.following?(user)
= button_to 'フォロー解除', relationship_path(current_user.relationships.find_by(follower: user)), method: :delete
- else
= button_to 'フォロー', relationships_path(follower: user)
following?
もモデルメソットです。
model
user
モデルのモデルメソッドとして、follow
、unfollow
、following?
メソッドを、それぞれ定義します。
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
# 今回追記したモデルメソッド
def follow(other_user)
return if self == other_user
relationships.find_or_create_by!(follower: other_user)
end
def following?(user)
followings.include?(user)
end
def unfollow(relathinoship_id)
relationships.find(relathinoship_id).destroy!
end
end
なお、このクラスの上方にあるのは、前回の記事で適宜した複雑なアソシエーションです。
非同期ではない通信での実装完了
ここまでで、Ajaxではないフォロー機能は完成しています。デモ用にredirect_to
で画面を遷移させているので、若干非同期っぽくも見えますが、「フォロー」ボタンを押して画面をリロード後「フォロー解除」ボタンが現れます。
Ajaxでの実装
それでは、これを非同期の通信にしていきます。こちらもさらっと書いていますので、どうしてそうなるのかが気になる方は、こちらの記事をご覧ください。
remote: trueでajaxの投稿をPOSTをするよ。
html部分
まずは、ボタン部分はremote: true
オプションをつけて、パーシャルに切り出します。
= button_to 'フォロー', relationships_path(follower: user), remote: true
= button_to 'フォロー解除', relationship_path(current_user.relationships.find_by(follower: user)), method: :delete, remote: true
# もともとボタンのあったビューファイル
- if logged_in? && current_user != user
div id="follow-button-#{user.id}"
- if current_user.following?(user)
= render 'relationships/unfollow_button', user: user
- else
= render 'relationships/follow_button', user: user
**.js.erb
ファイルを作る
動的に呼び出す部分のファイルは、**.js.erb
ファイルに書き出します。
$("#follow-button-<%= @other_user.id %>").html("<%= j(render 'unfollow_button', user: @other_user) %>")
$("#follow-button-<%= @user.id %>").html("<%= j(render 'follow_button', user: @user) %>")
完成!
こちらで完成です^^Ajaxにするのはビューファイルを書き換えるだけで終わりました
シンプルー
感想など
フォロー・フォロー解除機能は、データ構造8割!といった感じで、DBとモデルの設計がやたら複雑なんですね〜
しっかり読み解けてよかったです^^