railsのグループ機能でグループ参加承認機能を実装したので記事にまとめてみました。
こんにちは!
Rails初学者の私ですが、先日グループ参加の承認機能を実装しましたので
その実装方法を記事にしてみました。
完成系のイメージ
①ゲストユーザーで加入申請をします。
②加入申請後はボタンが変わり、申請取消しとなっています。
こちらを押すことで申請がキャンセルされ、加入申請ボタンに戻ります。
③グループオーナーでログインし直して、承認待ち一覧から加入許可ボタンを押せば、グループメンバーに加わることが出来ます。
前提
- グループ機能が実装済み
- Deviceでuserログインができる
リレーションの確認
基本的なグループ関連のリレーションの考えた方は
以前に記事にしている下記と同様になります。
https://qiita.com/fumiya1800/items/ec1b2f4d94c74ef041c7
今回はpermitテーブルを作成して
こちらのテーブルにグループ参加希望者を紐づけていきます。
permitに登録されている情報をもとに
group_userを作成することでグループにメンバーを加えていく形になります。
モデルの作成
$ rails g model Permit user:references group:references
class CreatePermits < ActiveRecord::Migration[6.1]
def change
create_table :permits do |t|
t.references :user, null: false, foreign_key: true
t.references :group, null: false, foreign_key: true
t.timestamps
end
end
end
忘れずに下記コマンドを行う
$ rails db:migrate
モデル編集
class User < ApplicationRecord
has_many :group_users, dependent: :destroy
has_many :permits, dependent: :destroy
has_many :groups, through: :group_users
end
class Group < ApplicationRecord
has_many :group_users, dependent: :destroy
has_many :permits, dependent: :destroy
has_many :users, through: :group_users
end
class Permit < ApplicationRecord
belongs_to :user
belongs_to :group
end
ルーティング編集
scope module: :public do
resources :groups, except: [:new] do
resource :permits, only: [:create, :destroy] #追加
resource :group_users, only: [:create, :destroy]
end
end
get "groups/:id/permits" => "groups#permits", as: :permits #追加
groupsにネストする形でpermitsを追加します。
今回resourceにて記述していますが
resourceメソッドは、コントローラの7つのアクションに対して、indexとid付きのパスが生成されないというものです。
今回は一人のユーザーは一つのグループに対して加入申請を送るという一回しかcreateが出来ないという仕様であるため、ユーザーidとグループidのみ分かればどのpermitを削除すればいいのかが特定できるため、permitのidは必要がなくresouceを使用しております。
また、groupsコントローラーで加入希望者の一覧を表示するpermitsアクションをルーティングに加えております。
コントローラーの編集
permitsコントローラを作成する
$ rails g controller public/permits
class Public::PermitsController < ApplicationController
before_action :authenticate_user!
def create
@group = Group.find(params[:group_id])
permit = current_user.permits.new(group_id: params[:group_id])
permit.save
redirect_to request.referer, notice: "グループへ参加申請をしました"
end
def destroy
permit = current_user.permits.find_by(group_id: params[:group_id])
permit.destroy
redirect_to request.referer, alert: "グループへの参加申請を取消しました"
end
end
createでは自分のuser_idとparams[gropu_id]をpermitに保存しています。
destroyではpermitsテーブルから自分のuser_idとparams[gropu_id]が登録されいるデータを探して、
そのデータを削除することでそのグループへの参加申請を取り消ししています。
次にgroup_usersコントローラーも編集します。
class Public::GroupUsersController < ApplicationController
before_action :authenticate_user!
def create
@group = Group.find(params[:group_id])
@permit = Permit.find(params[:permit_id])
@group_user = GroupUser.create(user_id: @permit.user_id, group_id: params[:group_id])
@permit.destroy #参加希望者リストから削除する
redirect_to request.referer
end
end
まずは@permitでグループに参加させたいユーザーの情報を保存します。
createで@permitに紐づいている情報をもとにgroup_userをコピーするような形で作成しております。
group_user作成後は@permitは不要となるので、削除を行っています。
次にgroupsコントローラーを編集します。
class Public::GroupsController < ApplicationController
before_action :authenticate_user!
before_action :ensure_correct_user, only: [:edit, :update, :destroy, :permits]
def permits
@group = Group.find(params[:id])
@permits = @group.permits.page(params[:page])
end
private
def group_params
params.require(:group).permit(:name, :introduction, :group_image)
end
# params[:id]を持つ@groupのowner_idカラムのデータと自分のユーザーIDが一緒かどうかを確かめる。
# 違う場合はグループ詳細ページを再表示させる。(オーナー以外は編集、削除、加入希望者ページの遷移はできない)before_actionで使用する。
def ensure_correct_user
@group = Group.find(params[:id])
unless @group.owner_id == current_user.id
redirect_to group_path(@group), alert: "グループオーナーのみ編集が可能です"
end
end
end
グループの参加希望者一覧を表示するpermitsアクションを追加しております。
また、permitsアクションはensure_correct_userメソッドでグループオーナーのみが使えるように設定しています。
その他のアクションは今回は割愛しております。気になる方は上記にもリンクを貼っているグループ作成の記事をご参照ください。
viewの編集
<% if @group.group_users.exists?(user_id: current_user.id) %>
<%= link_to 'グループ退出', group_group_users_path(@group), method: :delete, class: "btn btn-sm btn-danger", data: { confirm: "本当にグループを退出しますか?" } %>
<% elsif @group.permits.exists?(user_id: current_user.id) %>
<%= link_to '申請取消', group_permits_path(@group), method: :delete, class: "btn btn-sm btn-danger" %>
<% else %>
<%= link_to '加入申請', group_permits_path(@group), method: :post, class: "btn btn-sm btn-success" %>
<% end %>
<% if @group.owner_id == current_user.id %>
<div class="dropdown">
<button class="btn btn-sm btn-secondary dropdown-toggle"
type="button" id="dropdownMenu1" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
オーナー専用
</button>
<div class="dropdown-menu" aria-labelledby="dropdownMenu1">
<%= link_to "承認待ち一覧", permits_path(@group), class: "dropdown-item text-secondary bg-transparent" %> ⇦編集箇所
<%= link_to 'グループ編集', edit_group_path(@group), class: "dropdown-item text-success bg-transparent" %>
<%= link_to 'グループ削除', group_path(@group), method: :delete, class: "dropdown-item text-danger bg-transparent", data: { confirm: "本当に削除しますか?" } %>
</div>
</div>
<% end %>
グループ退出は今回の記事では割愛している部分となります。
グループに参加していない場合は加入申請ボタンが表示され、
グループに加入申請を送っている場合は申請取消しボタンが表示される記述となっています。
加入申請と申請取消しを押すことでpermitsのcreateとdestroyを行っています。
下の<% if @group.owner_id == current_user.id %>からの記述では
新たに承認待ち一覧ボタンを追加しています。
ここから、groupuコントローラーの#permitsアクションを呼び出してグループ参加希望者一覧を表示します。
その他の記述部分は今回の記事では割愛させていただきます。
次が最後となります。
<div class="container">
<h2 class="text-center"><%= @group.name %></h2>
<h4 class="text-center">承認待ち一覧</h4>
<div class="row mt-5">
<div class="col-11 col-md-12 mx-auto">
<% if @permits.present? %>
<div class="table-responsive">
<table class="table table-hover text-nowrap bg-light">
<thead class="thead-dark">
<tr>
<th>名前</th>
<th>部署</th>
<th>役職</th>
<th></th>
</tr>
</thead>
<tbody>
<% @permits.each do |permit| %>
<tr>
<td><%= link_to permit.user.name, public_user_path(permit.user.id), class: "text-dark" %></td>
<td>
<% if permit.user.department_id.present? %>
<%= permit.user.department.name %>
<% end %>
</td>
<td><%= permit.user.position %></td>
<td class="text-right">
<%= link_to '加入許可', group_group_users_path(@group, permit_id: permit.id), method: :post, class: "btn btn-sm btn-success" %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% else %>
<P class="text-center">承認待ちユーザーはいません。</P>
<% end %>
</div>
<div class="mx-auto mt-5"><%= paginate @permits %></div>
</div>
<div class="row mt-5">
<div class="col-md-9 mx-auto">
<p class="text-center"><%= link_to "戻る", group_path(@group), class: 'text-dark font-weight-bold' %></p>
</div>
</div>
</div>
groupsコントローラで@groupに紐ついたpermitを@permitsで定義しているので
each分で一つずつ取り出しています。
今回重要となるのが下記の部分になります。
<%= link_to '加入許可', group_group_users_path(@group, permit_id: permit.id), method: :post, class: "btn btn-sm btn-success" %>
ここでgroup_usersコントローラーのcreateアクションを呼び出しているのですが、
permit_id: permit.idという記述でデータをコントローラーに送っており、これによって該当するpermitのデータをコントローラー内で@permitに格納しています。
そうすることによって、該当するするユーザーのみをグループに参加させて、参加したユーザーは加入希望一覧から削除することが出来ます。
以上で完成になります。
今回の記事は以前に記事にしたグループ作成の記事から追加の部分のみを記載している為、
分からない箇所があった場合はぜひ、下記の記事も参考にしてみてください。
https://qiita.com/fumiya1800/items/ec1b2f4d94c74ef041c7
最後に
最後までご覧いただきありがとうございます。
初学者なので、間違っていることや、分かりづらい箇所もあるかと思います。
何かお気づきがあれば遠慮なくご指摘頂けると幸いです。
ここまで長くなりましたが
最後までお付き合いいただきましてありがとうございました!!