【Rails6】多対多のアソシエーションを利用したグループ参加機能の実装


多対多のアソシエーションを利用してグループ参加機能を実装する方法を紹介します。
目的

  • 前提条件
  • グループ参加機能の仕様
  • 動作確認
  • 実装手順1:モデルの作成
  • 実装手順2:コントローラーの作成
  • 実装手順3:ルーティングの設定
  • 実装手順4:ビューの作成

前提条件

Rails 6.0.3.5

グループ参加機能の仕様

承認制ではなく、グループに参加するボタンをクリックすると、データベースにユーザーIDとグループIDに組み合わせが保存されて参加できる簡単なものになります。

動作確認

  • 「Join this group」をクリック
  • Are you sure to join this group?」というメッセージが出る
  • OKをクリックするとグループのトップページに移動する
  • 「Notice: joined the group!!」のフラッシュメッセージが出る。

これでグループへの参加が完了です。

それでは説明していきます。
グループ参加機能の実装に関係する記述以外は省略しているのでご承知おきください。

実装手順1:モデルの作成

用意するモデル(テーブル)はuser, group, user_groupの3つです。

app/models/user.rb
class User < ApplicationRecord
  has_many :user_groups
  has_many :groups, through: :user_groups
end
app/models/group.rb
class Group < ApplicationRecord
  has_many :user_groups
  has_many :users, through: :user_groups
end
app/models/user_group.rb
class UserGroup < ApplicationRecord
  belongs_to :user
  belongs_to :group

  validates :user_id, uniqueness: { scope: :group_id }
end

実装手順2:コントローラーの作成

groups_controlleruser_groups_controllerを以下のように記述します。グループ参加機能の実装には、users_controllerへの記述は特にありません。

app/controllers/groups_controller.rb
class GroupsController < ApplicationController
  def show
    @group = Group.find(params[:id])
    # UserGroupテーブルからログインユーザーと、詳細を表示しているグループの組み合わせを探します。
    # 組み合わせがなければnilを返します。
    @userGroup = UserGroup.find_by(user_id: current_user.id, group_id: params[:id])
  end
app/controllers/user_groups_controller.rb
class UserGroupsController < ApplicationController
  def create
    @userGroup = UserGroup.new(user_id: current_user.id, group_id: params[:group_id])
    if @userGroup.save
      # グループ参加後のリダイレクト先を指定
      redirect_to XXXX_path(@userGroup.group_id), notice: 'joined the group!!'
    end
  end
end

実装手順3:ルーティングの設定

user_groups_controller.rbparams[:group_id]をを使用するアクションを定義しているため、user_groupsコントローラーgroupsコントローラーにネストさせます。

config/routes.rb
Rails.application.routes.draw do
  resources :groups, only: :show do
    resources :user_groups, only: :create
  end
end

実装手順4:ビューの作成

ボタンの部分の記述だけ掲載します。
条件分岐で@userGroupに値が入っていれば、グループに参加しているのでグループメンバーしか見られない情報を表示し、値が入っていなければで「Join the group」ボタンを表示します。

app/views/groups/show.html.erb
<% if @userGroup.present? %>
  <div class="form-group">
    <div class="schedule-item">
      <div class="schedule-item-header text-left">
        <p class="bold">Task</p>
      </div>
      <div class="schedule-item-body text-left">
        <p><%= @group.task %></p>
      </div>
    </div>
  </div>
<% else %>
  <div class="actions">
    <%# user_groupsコントローラーのcreateアクションを実行するリンク %>
    <%= link_to 'Join this group',  group_user_groups_path(@group.id), method: :post, data: { confirm: 'Are you sure to join this group?'}, class:"sign-up-btn" %>
  </div>
<% end %>

これでグループ参加機能の実装完了です。