11
9

More than 3 years have passed since last update.

Railsでユーザーフォロー機能を実装する(Ajax使うよ)②

Last updated at Posted at 2021-01-17

何をしたか

この記事は、こちらの記事の続きです。

▼前回の記事
Railsでユーザーフォロー機能を実装する(Ajax使うよ)①

Railsの課題を実施しています。Userのフォロー機能を実装することになりました。
前回までの記事では、マイグレーションファイルの作成と、モデルファイルへのアソシエーションの定義が終わったので、今回はcontrollerview、そしてモデルメソッドを追加していきます。

なお、実行環境は以下の通りです。

  • Rails 5.2.3
  • Ruby 2.6.0

ゴール

今回のゴールはこんな感じです。ボタンを押すとサクサクと非同期でフォロー/フォロー解除のボタンが現れます。

Image from Gyazo

仕様

この機能の使用は以下の通りです。

  • ユーザーは他のユーザーをフォローできる
  • 同じユーザーを2回フォローはできない
  • ユーザーは自分をフォローできない

データ構造

また、DBの設計は下記の通りです。
Image from Gyazo

なぜこうなるのかは、前回の記事を読んでくださいね:relaxed:

非同期ではない実装

まずは、非同期ではない形で実装していきます。
controllermodelともにさらっと書いていきますので、詳しく知りたい方はこの辺の記事をご覧になると良いかもしれません。

controller

controllerの記載は以下の通りです。

relationships_controller.rb
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

followunfollowはモデルメソッドです。後ほど解説します。

view

ビューファイルの記述は以下の通りです。なお、装飾のためのクラスは省いています。なお、user(それぞれのユーザー)を表す変数がここに表示しているコードの外から渡っているものとします。

view
- 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モデルのモデルメソッドとして、followunfollowfollowing?メソッドを、それぞれ定義します。

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で画面を遷移させているので、若干非同期っぽくも見えますが、「フォロー」ボタンを押して画面をリロード後「フォロー解除」ボタンが現れます。

Image from Gyazo

Ajaxでの実装

それでは、これを非同期の通信にしていきます。こちらもさらっと書いていますので、どうしてそうなるのかが気になる方は、こちらの記事をご覧ください。

remote: trueでajaxの投稿をPOSTをするよ。

html部分

まずは、ボタン部分はremote: trueオプションをつけて、パーシャルに切り出します。

views/relationships/_follow_button.html.slim
= button_to 'フォロー', relationships_path(follower: user), remote: true
views/relationships/_unfollow_button.html.slim
= 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ファイルに書き出します。

views/relationships/create.js.erb
$("#follow-button-<%= @other_user.id %>").html("<%= j(render 'unfollow_button', user: @other_user) %>")
views/relationships/destroy.js.erb
$("#follow-button-<%= @user.id %>").html("<%= j(render 'follow_button', user: @user) %>")

完成!

こちらで完成です^^Ajaxにするのはビューファイルを書き換えるだけで終わりました:relaxed:
シンプルー:sparkles:

Image from Gyazo

感想など

フォロー・フォロー解除機能は、データ構造8割!といった感じで、DBとモデルの設計がやたら複雑なんですね〜
しっかり読み解けてよかったです^^

11
9
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
11
9