この記事では、Railsでグループオーナーが作成したグループに、ユーザーがグループに参加・離脱できるようなグループ作成機能を実装する方法をまとめています。
作製するモデルのアソシエーション
今回作成するテーブルはGroupUser
とGroup
のふたつです。
Groupテーブル
グループの情報を保存します(name
、introduction
、image_id
など)。
owner_id
フィールドにより、グループの所有者(ユーザー)を表します。
GroupUserテーブル
中間テーブルで、ユーザーがどのグループに所属しているかを管理します。
user_id
とgroup_id
でUser
とGroup
を関連付けています。
実装手順
1. GroupモデルとGroupUserモデルの作成
まず、Group
モデルと、グループに所属するユーザーを管理するGroupUser
モデルを作成します。
rails generate model Group name:string introduction:text image_id:string owner_id:integer
class CreateGroupUsers < ActiveRecord::Migration[6.0]
def change
create_table :group_users do |t|
t.integer :user_id
t.integer :group_id
t.timestamps
end
end
end
rails generate model GroupUser user:references group:references
class CreateGroups < ActiveRecord::Migration[6.0]
def change
create_table :groups do |t|
t.string :name
t.text :introduction
t.integer :owner_id
t.timestamps
end
end
end
2. アソシエーションとバリデーションの追加
次に、User
モデル、Group
モデル、GroupUser
モデルにアソシエーションとバリデーションを追加します。
class User < ApplicationRecord
has_many :group_users, dependent: :destroy
has_many :groups, through: :group_users # user.groupsが利用可能に
end
class GroupUser < ApplicationRecord
belongs_to :user
belongs_to :group
validates_uniqueness_of :group_id, scope: :user_id # 同じユーザーが同じグループに複数回参加しないように
end
class Group < ApplicationRecord
has_many :group_users, dependent: :destroy
has_many :users, through: :group_users # group.usersが利用可能に
# バリデーション
validates :name, presence: true
validates :introduction, length: { maximum: 50 }
validates :owner_id, presence: true
has_one_attached :group_image
# 画像を取得するメソッド
def get_group_image(weight, height)
unless self.group_image.attached?
file_path = Rails.root.join('app/assets/images/no_image.jpg')
group_image.attach(io: File.open(file_path), filename: 'default-image.jpg', content_type: 'image/jpeg')
end
self.group_image.variant(resize_to_fill: [weight, height]).processed
end
# ユーザーがグループに参加しているかを判定
def joined_by?(user)
group_users.exists?(user_id: user.id)
end
end
3. ルーティングの設定
次に、ルーティングにグループとそのユーザー管理用のルートを追加します。
resources :groups do
resource :group_users, only: [:create, :destroy] # グループへの参加と離脱
end
4. グループ作成・一覧ページへのリンクを追加
ユーザー一覧ページに、グループ作成ページへのリンクとグループ一覧ページへのリンクを追加します。
:
<div class='col-md-8 offset-md-1'>
<h2>Users</h2>
<%= link_to "グループを作成する", new_group_path %> | <%= link_to "グループ一覧", groups_path %>
<%= render 'index', users: @users %>
</div>
</div>
</div>
5. Viewファイルの作成
<div class='container'>
<div class='row'>
<div class="col-sm-12 col-md-8 col-lg-5 px-5 px-sm-0 mx-auto">
<h2>Group Creation</h2>
<%= form_with model: @group, local: true do |f| %>
<div class="form-group">
<%= f.label :name %>
<%= f.text_field :name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :introduction %>
<%= f.text_area :introduction, class: "form-control" %>
</div>
<%= f.submit "Create Group", class: "btn btn-primary" %>
<% end %>
</div>
</div>
</div>
<div class='container px-5 px-sm-0'>
<div class='row'>
<div class='col-md-8 offset-md-1'>
<h2>Groups</h2>
<%= render 'index', groups: @groups %>
</div>
</div>
</div>
<table class='table'>
<thead>
<tr>
<th></th>
<th>グループ名</th>
<th>紹介文</th>
<th></th>
</tr>
</thead>
<tbody>
<% @groups.each do |group| %>
<tr>
<td><%= image_tag group.get_group_image(80, 80) %></td>
<td><%= link_to group.name, group_path(group.id) %></td>
<td><%= group.introduction %></td>
<% if group.owner_id == current_user.id %>
<td><%= link_to 'Edit', edit_group_path(group.id), class: "btn btn-sm btn-success" %></td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
6. Controllerの設定
最後に、グループ管理用のGroupsController
とGroupUsersController
を作成します。
class GroupsController < ApplicationController
before_action :authenticate_user!
before_action :ensure_correct_user, only: [:edit, :update, :destroy] #グループオーナーでないと:edit, :update, :destroyは使えない
def new
@group = Group.new
end
def create
@group = Group.new(group_params) #グループ新規作成
@group.owner_id = current_user.id #owner_idの代入
if @group.save
redirect_to groups_path, notice: "You have created group successfully."
else
render 'new'
end
end
def index
@groups = Group.all
end
def show
@group = Group.find(params[:id])
@users = @group.users #@groupに入っているユーザーのみ格納
@owner_user = User.find(@group.owner_id) #グループオーナーを見つけて格納
end
def edit
end
def update
if @group.update(group_params)
redirect_to groups_path, notice: "You have updated group successfully."
else
render 'edit'
end
end
private
def group_params
params.require(:group).permit(:name, :introduction, :group_image)
end
def ensure_correct_user #グループオーナーかどうかを判断する
@group = Group.find(params[:id])
unless @group.owner_id == current_user.id
redirect_to groups_path
end
end
end
class GroupUsersController < ApplicationController
# グループにユーザーを参加させるアクション
def create
# 加入しようとしているグループを検索する
group = Group.find(params[:group_id])
# group_userにuser_id、group_idを格納
group_user = current_user.group_users.new(group_id: group.id)
# ユーザーをグループに加入
group_user.save
redirect_to groups_path
end
# グループからユーザーを脱退させるアクション
def destroy
# 脱退しようとしているグループを検索する
group = Group.find(params[:group_id])
# group_userにuser_id、group_idを格納
group_user = current_user.group_users.find_by(group_id: group.id)
# ユーザーをグループから脱退させる
group_user.destroy
redirect_to groups_path
end
end