2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rails7系でフォローリクエスト機能を作る

Last updated at Posted at 2025-06-24

はじめに

こんにちは。現在プログラミングスクール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

受け取ったフォローリクエストの承認、拒否、自分が送ったフォローリクエストの送信キャンセルを処理として書いています。

終わりに

以上でフォローリクエスト機能が完成しました🎉
少し複雑な部分もありますが、冷静に分解していけば大丈夫です。
この記事が皆様の開発の一助となれば幸いです。
ご覧いただきありがとうございました!

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?