実施したいこと
申請・承認の仕組みを実装する
具体的には?
「ユーザ(小西)が、とあるコミュニティに加入したいと思いますが、
加入にはコミュニティの管理者(大西)の承認が必要になる」といったケースでの実装例です。
ざっくりイメージ
処理フロー概要
- 該当コミュニティ画面にて、ユーザ(小西)が「申請」ボタンを押す。
- 該当コミュニティの申請待ちリストにユーザ(小西)が追加される。管理者が、該当コミュニティの申請待ち一覧画面を見て、申請者を承認するかどうか判断。
- 申請を承認する場合は、承認ボタンを押す。申請ユーザがコミュニティに加入し、申請待ちリストからは消去される。
- 申請を却下する場合は、否認ボタンを押す。申請待ちリストから消去される。
環境
Ruby 2.6.5
Rails 5.2.4.2
mysql 5.7
現在のアプリケーション機能の構成
ユーザが色々なコミュニティに所属できるアプリケーションを作成しています。
- userモデル:アプリの利用ユーザ情報を保持
- communityモデル:コミュニティ情報を保持
- belongingモデル:ユーザが所属するコミュニティ情報を保持。ユーザは複数のコミュニティに所属できます。
※userとcommunityはbelongingモデルを介して多対多の関係となっています。
以下ソースは関連部分のみ抜粋
class User < ApplicationRecord
has_many :belongings, dependent: :destroy
has_many :applies, dependent: :destroy
has_many :communities, through: :belongings # ユーザが所属しているコミュニティ
end
class Community < ApplicationRecord
has_many :belongings, dependent: :destroy
has_many :applies, dependent: :destroy
has_many :users, through: :belongings # コミュニティに所属しているユーザ
# ユーザがコミュニティに所属していればtrueを返す
def user_belonging?(user)
users.include?(user)
end
end
class Belonging < ApplicationRecord
belongs_to :user
belongs_to :community
validates :user_id, presence: true
validates :community_id, presence: true
validates :user_id, uniqueness: { scope: :community_id }
validates :community_id, uniqueness: { scope: :user_id }
end
Applyモデルを追加し、申請状況を保持させる。
Applyモデルのルーティングやコントローラ、ビューファイルを追加します。
class Apply < ApplicationRecord
belongs_to :user
belongs_to :community
validates :user_id, presence: true
validates :community_id, presence: true
validates :user_id, uniqueness: { scope: :community_id}
validates :community_id, uniqueness: { scope: :user_id}
end
ルーティング(抜粋)
Rails.application.routes.draw do
root 'home#index'
resources :communities do
resources :applies, only: %i[index create destroy]
resources :belongings, only: %i[index create destroy]
end
resources :users, only: [:index, :show]
end
1.該当コミュニティ画面にて、ユーザ(小西)が「申請」ボタンを押す。
- 申請ボタン(コミュニティ詳細画面に配置)
(views/communities/show.html.erb)
<!-- ログインユーザが当該コミュニティに所属している場合 -->
<% if @community.user_belonging?(current_user) %>
<%= link_to '退会する', community_belonging_path(@community, @belonging), method: :delete, data:{ confirm: "コミュニティ「#{@community.name}」を退会します。よろしいですか?" } ,class:"mini-red-link-btn font-bold text-line-none" %>
<!-- 当該コミュニティには所属していないが、ログインはしている場合 -->
<% elsif current_user %>
<% if @apply %>
<%= link_to '申請取消', community_apply_path(@community, @apply), method: :delete, class: "mini-red-link-btn font-bold text-line-none" %>
<% else %>
<%= link_to '加入申請', community_applies_path(@community), method: :post, class: "mini-green-link-btn font-bold text-line-none" %>
<% end %>
申請ボタンは、以下の条件で表示させます。
- コミュニティにすでに加入している場合:退会するボタンが表示
- コミュニティにまだ加入していない場合:申請ボタンが表示
- すでに加入申請済の場合:申請取消ボタンを表示
- 申請ボタン押下後(createアクションが発生)
class AppliesController < ApplicationController
def create
current_user.applies.create(community_id: apply_params[:community_id])
redirect_to community_url(apply_params[:community_id]), notice: "加入申請しました"
end
private
def apply_params
params.permit(:community_id)
end
end
Ajaxは使わず、同画面へのリダイレクトが発生するようにします。
(Ajaxを使ってもよさそう)
これで、applyモデルに申請情報が追加されました。
2.該当コミュニティの申請待ちリストにユーザ(小西)が追加される。管理者が、該当コミュニティの申請待ち一覧画面を見て、申請者を承認するかどうか判断。
次に、申請待ちリストの確認です。
管理者(大西)が該当コミュニティ画面から「申請待ち一覧画面」を開きます。
- 申請待ち画面へのリンク(コミュニティ詳細画面)
(views/communities/show.html.erb)
<% if user_admin_flg(current_user,@community) == 1 %>
<%= link_to "承認待ち一覧", community_applies_path(@community), class:"btn btn-primary" %>
<% end %>
リンクをクリックすると、申請待ち一覧画面が開きます。
- 申請待ち一覧画面
(views/applies/index.html.erb)
<div class="container applicant-wrapper">
<h3>承認待ちユーザ一覧</h3>
<div class="row">
<% @applies.each do |app| %>
<div class="col-6">
<% if app.user.image.attached? %>
<%= link_to app.user.image, user_path(app.user), class:"user-icon" %>
<% else %>
<%= link_to user_path(app.user) do %>
<%= image_tag ("no_image.png"), class:"user-icon" %>
<% end %>
<% end %>
 <%= app.user.username %><br>
<%= link_to "承認", community_belongings_path(app.community, user_id: app.user.id, apply_id: app.id), method: :post, class:"mini-green-link-btn font-bold text-line-none" %>
<%= link_to "却下", community_apply_path(app.community, app), method: :delete, class:"mini-red-link-btn font-bold text-line-none" %>
<br>
</div>
<% end %>
</div>
</div>
applyモデルから、該当コミュニティへの申請情報を一覧表示させています。
同画面において、承認ボタン、却下ボタンも配置しています。
3.申請を承認する場合は、承認ボタンを押す。申請ユーザがコミュニティに加入し、申請待ちリストからは消去される。
承認ボタンを押すと、以下の処理が実行されます。
- belongingsコントローラのcreateアクションが実行される。
- 申請待ち一覧から削除するため、Applyモデルの該当の申請情報が削除される。
- 申請待ち一覧画面へリダイレクト
- belongingsコントローラ
class BelongingsController < ApplicationController
def create
@belonging = Belonging.create(community_id: belonging_params[:community_id], user_id: belonging_params[:user_id])
Apply.find(belonging_params[:apply_id]).destroy!
redirect_to community_applies_url(@belonging.community), notice:"「#{@belonging.user.username}」が、コミュニティ:#{@belonging.community.name}へ加入しました。"
end
private
def belonging_params
params.permit(:community_id, :user_id, :apply_id)
end
end
4.申請を却下する場合は、否認ボタンを押す。申請待ちリストから消去される。
却下ボタンを押すと、以下の処理が実行されます。
- 申請待ち一覧から削除するため、appliesコントローラのdestroyアクションが実行され、Applyモデルの該当の申請情報が削除される。
- 申請待ち一覧画面へリダイレクト
- appliesコントローラ
class AppliesController < ApplicationController
def destroy
@apply = Apply.find(params[:id])
@apply.destroy!
@comminity = Community.find(params[:community_id])
redirect_to community_url(@comminity), notice: "加入申請を取り消しました"
end
end
以上になります。
文中でappliesコントローラとbelongingsコントローラはアクション毎に分けて紹介していますが、最後に全部まとめて載せておきます。
class AppliesController < ApplicationController
def create
current_user.applies.create(community_id: apply_params[:community_id])
redirect_to community_url(apply_params[:community_id]), notice: "加入申請しました"
end
def destroy
@apply = Apply.find(params[:id])
@apply.destroy!
@comminity = Community.find(params[:community_id])
redirect_to community_url(@comminity), notice: "加入申請を取り消しました"
end
def index
@applies = Apply.where(community_id: params[:community_id])
end
private
def apply_params
params.permit(:community_id)
end
end
class BelongingsController < ApplicationController
def create
@belonging = Belonging.create(community_id: belonging_params[:community_id], user_id: belonging_params[:user_id])
Apply.find(belonging_params[:apply_id]).destroy!
redirect_to community_applies_url(@belonging.community), notice:"「#{@belonging.user.username}」が、コミュニティ:#{@belonging.community.name}へ加入しました。"
end
def destroy
@belonging = Belonging.find(params[:id])
@belonging.destroy!
@comminity = Community.find(params[:community_id])
redirect_to community_url(@comminity), notice: "コミュニティ「#{@comminity.name}」を退会しました。"
end
private
def belonging_params
params.permit(:community_id, :user_id, :apply_id)
end
end
class CommunitiesController < ApplicationController
skip_before_action :authenticate_user!, only: %i[index show]
before_action :validate_community, only: %i[edit update destroy]
def index
# 検索オブジェクト
@search = Community.ransack(params[:q])
# 検索結果
@communities = @search.result
@communities = @communities.page(params[:page]).per(12)
end
def show
@community = Community.find(params[:id])
@belongings = Belonging.where(community_id: @community.id)
@code = Code.all
# 検索オブジェクト
@search = Post.ransack(params[:q])
# 検索結果
@posts = @search.result
@posts = @posts.where(community_id: @community.id)
@posts = @posts.page(params[:page]).per(9)
return unless current_user
@belonging = Belonging.find_by(community_id: @community.id, user_id: current_user.id)
@apply = Apply.find_by(community_id: @community.id, user_id: current_user.id)
end
def new
@community = Community.new
@code = Code.all
end
def edit
@community = Community.find(params[:id])
@code = Code.all
end
def create
@community = current_user.communities.build(community_params)
if @community.save
Belonging.create!(community_id: @community.id, user_id: current_user.id, admin_flg: '1')
redirect_to communities_url, notice: "グループ「#{@community.name}」を登録しました。"
else
render :new
end
end
def update
community = current_user.communities.find(params[:id])
community.update!(community_params)
redirect_to community_url(community), notice: "グループ「#{community.name}」を更新しました。"
end
def destroy
community = Community.find(params[:id])
community.destroy
redirect_to communities_url, notice: "グループ「#{community.name}」を削除しました。"
end
private
def community_params
params.require(:community).permit(:name, :create_user_id, :publish_flg, :description, :image)
end
def validate_community
@community = Community.find(params[:id])
redirect_to community_path(@community), alert: "作成ユーザのみ変更操作ができます" if @community.create_user_id != current_user.id
end
end
初心者が自分の頭でロジック・コードを考えて実装したので、
間違いや、もっと適した実装方法があるかもしれません。
ぜひご指摘いただけますと幸いです。
この実装で、多対多の関係のことがより理解できたなと思います。