はじめに
こんにちは。現在プログラミングスクールRUNTEQにて学習中の者です。
開発したアプリにて、フォローリクエスト機能を作成したので、備忘録として記事を書きました。
まだまだ初学者なので、間違っていることも多いと思います。
ご指摘ご感想大歓迎ですので、お気軽にコメントいただけたら嬉しいです!
では本題に移ります。
目標
- 非公開ユーザーに、フォローリクエストを送信する機能を作成する
環境
- Ruby on Rails 7.2
- Ruby 3.3.6
- Windows11
- Docker
前提
- ユーザーの登録機能が完成している
- ユーザーテーブルに非公開カラムがある
- フォロー機能が完成している
手順
フォローリクエストテーブルを作成する
フォローリクエストを管理するテーブルを作成します。
今回は、テーブル名をfollow_requests
にしました。
$ docker compose exec web rails g model follow_requests
上記コマンドを打つと、空のマイグレーションファイルとモデルファイルが作成されます。
まず、マイグレーションファイルを見ていきましょう。
マイグレーションファイルの編集
マイグレーションファイルは以下のように編集しました。
class CreateFollowRequests < ActiveRecord::Migration[7.2]
def change
create_table :follow_requests do |t|
# フォローリクエストを送信したユーザーのID
t.uuid :requester_id, null: false
# フォローリクエストを受信したユーザーのID
t.uuid :requestee_id, null: false
# フォローリクエストの状態
# 0: 保留中, 1: 承認
t.integer :status, null: false, default: 0
t.timestamps
end
# 外部キー制約
add_foreign_key :follow_requests, :users, column: :requester_id, primary_key: :id
add_foreign_key :follow_requests, :users, column: :requestee_id, primary_key: :id
# ユニーク制約を追加
add_index :follow_requests, [ :requester_id, :requestee_id ], unique: true
end
end
ここで気を付けていただきたいポイントは以下の二つです。
- アソシエーション
- ユニーク制約
順に解説します。
アソシエーション
フォローリクエストは「ユーザーが他のユーザーにリクエストを送る」という 1対1の関係を表します。
ただし、ユーザー同士の関係を別のテーブル(follow_requests)で管理するため、中間テーブルのような構成になります。
フォロー機能のように「多対多の関係(=複数人をフォロー・される)」になることもありますが、1件のフォローリクエスト自体は「送信者と受信者の1対1の関係」です。
ユニーク制約
フォローリクエストを送った人(requester_id)とフォローリクエストを受け取った人(requestee_id)が重複しないようにします(ユニーク制約)。
こうすることで、自分が自分にフォローリクエストを送信しないようにしたり、また、同じユーザーが特定のユーザーに対して重複してフォローリクエストを送信することを防ぐことができます。
編集したら、以下のコマンドを打って、DBに反映させましょう。
$ docker compose exec web rails db:migrate
モデルファイルの編集
follow_request.rb
を編集します。
class FollowRequest < ApplicationRecord
belongs_to :requester, class_name: "User"
belongs_to :requestee, class_name: "User"
# 保留中:0
enum status: { pending: 0 }
validates :requester_id, presence: true
validates :requestee_id, presence: true
validates :requester_id, uniqueness: { scope: :requestee_id }
validate :cannot_request_self
private
def cannot_request_self
errors.add(:base, "自分自身にフォローリクエストを送ることはできません") if requester_id == requestee_id
end
end
モデルファイルでもユニーク制約をかけます。
こうすることで、ユーザー側にもエラーメッセージを表示できるようになり、親切です。
ユーザーモデルの編集
ユーザーとアソシエーションを持つので、ユーザーにも関連付けの記載をします。
class User < ApplicationRecord
#~~~割愛~~~
# フォローリクエストのアソシエーション(ここから)
has_many :follow_requests, foreign_key: :requester_id, dependent: :destroy
has_many :requested_users, through: :follow_requests, source: :requestee
has_many :inverse_follow_requests, foreign_key: :requestee_id, class_name: "FollowRequest", dependent: :destroy
has_many :requesters, through: :inverse_follow_requests, source: :requester
# フォローリクエストのアソシエーション(ここまで)
#~~~割愛~~~
validates :is_private, inclusion: { in: [ true, false ] }
# フォローリクエストを送信するときの機能
def send_request(user)
requested_users << user
end
# 自分が送ったリクエストを消す
def destroy_request(user)
requested_users.destroy(user)
end
# フォローリクエストをuserに送ったか?
def request?(user)
requested_users.include?(user)
end
# 受け取ったか?
def received_request?(user)
requesters.include?(user)
end
# 送ったフォローリクエストの取得
def sent_follow_request(user)
follow_requests.find_by(requestee_id: user.id)
end
# 受け取ったフォローリクエストの取得
def received_follow_request(user)
inverse_follow_requests.find_by(requester_id: user.id)
end
# フォローリクエストを拒否する
def reject_request(user)
requesters.destroy(user)
end
# リクエストをうけいれてレコードを消す
def accept_request(user)
user.follow(self)
requesters.destroy(user)
end
end
関連付けのあたりが少しごちゃごちゃしていて一見難しいですが、紐解いていけばシンプルです。
順に説明します。
-
follow_requests
は、自分が送ったフォローリクエスト -
requested_users
は、自分がフォローリクエストを送った相手 -
inverse_follow_requests
は自分が受け取ったフォローリクエスト -
requesters
は、自分に向けてフォローリクエストを送った相手
を表しています。
コントローラーの編集(フォロー)
follows_controller.rb
内にて、フォローリクエストを作成する処理を書きます。
class FollowsController < ApplicationController
def create
@user = User.find(params[:id])
if @user.is_private?
# 非公開ユーザーにフォローリクエストを送信
current_user.send_request(@user)
follow_request = current_user.follow_requests.find_by(requestee_id: @user.id)
redirect_to profile_posts_path(@user.id), notice: "フォローリクエストを送りました。。"
else
# ~~~割愛~~~
end
end
end
コントローラーの編集(フォローリクエスト)
コントローラーは以下のようにしました。
class FollowRequestsController < ApplicationController
before_action :set_follow_request, only: %i[accept reject cancel]
# フォローリクエストを承認
def accept
# フォロー承認+レコードを削除
current_user.accept_request(@user)
redirect_to profile_posts_path(@user), notice: "フォローリクエストを承認しました"
end
# フォローリクエストを拒否
def reject
current_user.reject_request(@follow_request.requester_id)
redirect_to profile_posts_path(@user), notice: "フォローリクエストを拒否しました", status: :see_other
end
# フォローリクエストをキャンセル
def cancel
# 自分が送ったリクエストをキャンセル
current_user.destroy_request(@follow_request.requestee_id)
redirect_to profile_posts_path(@follow_request.requestee_id), notice: "フォローリクエストをキャンセルしました", status: :see_other
end
private
def set_follow_request
@follow_request = FollowRequest.find(params[:follow_request_id])
@user = User.find_by(id: @follow_request.requester_id)
end
end
受け取ったフォローリクエストの承認、拒否、自分が送ったフォローリクエストの送信キャンセルを処理として書いています。
終わりに
以上でフォローリクエスト機能が完成しました🎉
少し複雑な部分もありますが、冷静に分解していけば大丈夫です。
この記事が皆様の開発の一助となれば幸いです。
ご覧いただきありがとうございました!