1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Railsで申し込み機能を実装する

Last updated at Posted at 2020-10-22

Railsを用いた投稿の申し込み機能とキャンセル機能の実装方法について解説します。
※ユーザー機能と投稿機能は実装済みであることを前提とします。

実装の流れ

大まかな流れとして、以下の手順で実装していきます。

  1. 投稿と申込者の中間テーブルを作成する
  2. モデル同士の関連付け(アソシエーション)を行う
  3. ルーティングを定義する
  4. コントローラーに申し込み機能とキャンセル機能を定義する
  5. 申込ボタンのビューを作成する

投稿と申込者の中間テーブルを作成する

投稿は複数人の申込者を持つことができ、ユーザーは複数の投稿に申し込むことができます。
そのため、投稿とユーザーは多対多の関係となり、中間テーブルが必要となります。

また、ここでは同じユーザーの中でも投稿者と申込者を区別するために、申込者をtakerと定義することにします。
投稿のモデル名をPostとすると、post_takersという中間テーブルを作れば良いということになります。

中間テーブルを作成するにはまず、ターミナルに以下のコマンドを打ちモデルを作成します。

$ rails g model PostTaker

出来上がったマイグレーションファイルを開き、以下のように記述します。

2020XXXXXXXXXX_create_post_takers.rb
class CreatePostTakers < ActiveRecord::Migration[6.0]
  def change
    create_table :post_takers do |t|
      t.references :post, foreign_key: true
      t.references :taker, foreign_key: {to_table: :users} 
      t.timestamps
    end
  end
end

ここでのポイントは、t.references :taker, foreign_key: {to_table: :users} の部分です。
このように書くと、中間テーブルの外部キーとして申込者であるtakerという名前を定義しつつ、usersテーブルを参照できます。
参考記事:https://qiita.com/publichtml/items/1fba15d8071fab66d043

モデル同士の関連付け(アソシエーション)を行う

次に、モデルにアソシエーションを記述します。

user.rb
class User < ApplicationRecord
  has_many :post_takers, foreign_key: "taker_id", dependent: :destroy
end
post.rb
class Post < ApplicationRecord
  has_many :post_takers, dependent: :destroy
  has_many :takers, through: :post_takers, dependent: :destroy
end
post_taker.rb
class PostTaker < ApplicationRecord
  belongs_to :taker, class_name: 'User', foreign_key: 'taker_id'
  validates_uniqueness_of :post_id, scope: :taker_id
end

post_taker.rbのbelongs_to :taker, class_name: 'User', foreign_key: 'taker_id'は、
このような書き方をすることでUserモデルに紐付いた申込者と関連付けができます。
参考記事:https://qiita.com/gyu_outputs/items/421cc1cd2eb5b39e20ad

また、validates_uniqueness_of :post_id, scope: :taker_idは、
投稿と申込者の同じ組み合わせが2つ以上登録されないようにするため、このようなバリデーションを記述しています。

ルーティングを定義する

申し込むというアクションをtake、キャンセルというアクションをcancelとしてルーティングに以下のように記述します。

routes.rb
Rails.application.routes.draw do
  resources :posts do
    member do
      get 'take'
      get 'cancel'
    end
  end
end

上記では、パスに投稿のidを含めるためmemberを使っています。
参考記事:https://qiita.com/hirokihello/items/fa82863ab10a3052d2ff

コントローラーに申し込み機能とキャンセル機能を定義する

コントローラーに以下の記述を追加します。
※ここでは投稿詳細ページに申込ボタンを表示させることを前提として書いていますが、各々の状況に合わせて書き換えてください。

posts_controller.rb
class PostsController < ApplicationController
  before_action :set_post

  def show
    @user = @post.user
  end

  def take
    # 該当の投稿とログイン中のユーザーとの中間テーブルのレコードを作成する
    PostTaker.create(post_id: @post.id, taker_id: current_user.id)
    # フラッシュメッセージを表示(フラッシュメッセージを表示させない場合は書かなくて大丈夫です)
    flash[:notice] = '申し込みが完了しました。'
    # 投稿の詳細ページにリダイレクト
    redirect_to action: "show"
  end

  def cancel
    # 中間テーブルの中から、該当の投稿とログイン中のユーザーによるレコードを抽出する
    post_taker = PostTaker.find_by(post_g_id: @post.id, taker_id: current_user.id)
    post_taker.destroy
    flash[:notice] = 'キャンセルが完了しました。'
    redirect_to action: "show"
  end

  private

  def set_post
    @post = Post.find(params[:id])
  end
end

申込ボタンのビューを作成する

最後に、申込ボタンのビューを作成したら完成です!
以下のコードはhamlで記述しています。

show.html.haml
-# 投稿者とログイン中のユーザーが一致しない場合のみボタンを表示させる
- if @user.id != current_user.id
  -# 該当の投稿の申込者の中に、ログイン中のユーザーが含まれているか否かで場合分けする
  - if @post.taker_ids.include?(current_user.id)
    = link_to "申し込みをキャンセルする", cancel_posts_path(@post.id), data: { confirm: "こちらの投稿の申し込みをキャンセルしますか?" }
  - else
    = link_to "申し込む", take_posts_path(@post.id), data: { confirm: "こちらの投稿に申し込みますか?" }
1
3
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
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?