Rails グループ作成
グループ作成機能
グループを作れるようにする流れについてまとめました🏋🏻
要件
- グループを作成する
- ユーザーの一覧画面に group を作成するボタンを作る
- グループを作成する機能の実装
- 作成した人がグループオーナーになる
- グループ一覧画面を作成
- 自分がグループオーナーの場合は、Edit リンクを出す
- ユーザーの一覧画面に group 一覧のボタンを作る
- グループ編集機能を作成
- グループオーナーだけ編集可能にする
- グループ詳細画面の作成(メンバーはまだ実装しなくて良い)
完成
index
show
create
edit
ER図
User:Group = 1:N
Group:User = 1:N
なので中間テーブルが必要となります!
中間テーブルは、GroupUserとして外部キーuser_idとgroup_idを持たせます。
Groupテーブルのカラムには
id
name
introduction
image_id(グループ画像を設定するため)
owner_id(グループ作成者をowner_idにする)
コントローラーとモデルを作成
rails g controller groups
rails g model group
rails g model group_user
中間テーブルはgroupとuserの外部キーを持ってくれているだけで、何かするわけでないのでコントローラーは不要!
migrationファイルの編集
class CreateGroups < ActiveRecord::Migration[6.1]
def change
create_table :groups do |t|
t.string :name
t.text :introduction
t.string :image_id
t.integer :owner_id
t.timestamps
end
end
end
class CreateGroupUsers < ActiveRecord::Migration[6.1]
def change
create_table :group_users do |t|
t.references :user, foreign_key: true
t.references :group, foreign_key: true
t.timestamps
end
end
end
⇧
group_usersに外部キーとしてuser_idとgroup_idを登録している!
reference型では外部キー制約をつけるときに、foreign_key: trueが使えるようになる!
🌱わかりやすい記事
🔑外部キーの設定法忘れずに行う!
rails db:migrate
アソシエーション記述
class GroupUser < ApplicationRecord
belongs_to :user
belongs_to :group
end
class Group < ApplicationRecord
has_many :group_users, dependent: :destroy
belongs_to :owner, class_name: 'User'
has_many :users, through: :group_users
validates :name, presence: true
validates :introduction, presence: true
has_one_attached :group_image
def is_owned_by?(user)
owner.id == user.id
end
end
class User < ApplicationRecord
has_many :group_users, dependent: :destroy
end
has_many :groups, through: :group_users
userはgroup_usersを通してgroupsにアクセスできるので、
この記述も必要かな?と思ったのですが、回答を見たところなくても問題なく動作したので消しました。
現時点ではあってもなくても大丈夫っぽいです🧐
🌱データ管理についてわかりやすい記事
ルーティングの設定
resources :groups, only: [:new, :index, :show, :create, :edit, :update]
コントローラー作成
class GroupsController < ApplicationController
before_action :authenticate_user!
before_action :ensure_correct_user, only: [:edit, :update]
def index
@book = Book.new
@groups = Group.all
@user = User.find(current_user.id)
end
def show
@book = Book.new
@group = Group.find(params[:id])
@user = User.find(params[:id])
end
def new
@group = Group.new
end
def create
@group = Group.new(group_params)
@group.owner_id = current_user.id
if @group.save
redirect_to groups_path, method: :post
else
render 'new'
end
end
def edit
end
def update
if @group.update(group_params)
redirect_to groups_path
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
indexとshowアクションにはUser infoとNew book投稿も表示させるため、@userと@bookを定義しています。
最後のdef ensure_correct_user
は、指定されたグループが現在ログインしているユーザーの所有するものであることを確認し、ユーザーが他人のグループの編集や削除を行えないようにする記述。
ビューページ
⇧
この部分
<%=link_to "グループを作成する",new_group_path %> |
<%=link_to "グループ一覧",groups_path %>
その他のビューページ
<div class='container'>
<div class='row'>
<div class="col-sm-12 col-md-8 col-lg-5 px-5 px-sm-0 mx-auto">
<h3>Editing Group</h3>
<%= render 'form', group: @group %>
</div>
</div>
<%= form_with model: group, local: true do |f| %>
<div class="form-group">
<%= f.label :グループ名 %>
<%= f.text_field :name, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :紹介文 %>
<%= f.text_area :introduction, class: 'form-control' %>
</div>
<div class="form-group">
<%= f.label :グループ画像 %>
<%= f.file_field :group_image, class: "form-control-file" , accept: "image/*" %>
</div>
<div class="form-group">
<%= f.submit class: 'btn btn-info' %>
</div>
<% end %>
<div class='container px-5 px-sm-0'>
<%= flash[:notice] %>
<div class='row'>
<div class='col-md-3'>
<%= render partial: "partial/user_info", locals: { user: @user }%>
<%= render partial: "partial/new_books", locals: { book: @book } %>
</div>
<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 colspan="3"></th>
</tr>
</thead>
<tbody>
<% groups.each do |group| %>
<tr>
<td>
<% if group.group_image.attached? %>
<%= image_tag group.group_image,size:"50x50" %>
<% else %>
<%= image_tag 'no_image', size: "50x50" %>
<% end %>
</td>
<td><%= link_to group.name,group_path(group) %></td>
<td><%= group.introduction %></td>
<% if group.is_owned_by?(current_user) %>
<td><%= link_to 'Edit', edit_group_path(group), class: "group_#{group.id} btn btn-sm btn-success" %>
</td>
<% end %>
</tr>
<% end %>
</tbody>
</table>
<div class='container'>
<div class='row'>
<div class="col-sm-12 col-md-8 col-lg-5 px-5 px-sm-0 mx-auto">
<h3>Create Group</h3>
<%= render 'form', group: @group %>
</div>
</div>
</div>
<div class='container px-5 px-sm-0'>
<%= flash[:notice] %>
<div class='row'>
<div class='col-md-3'>
<%= render partial: "partial/user_info", locals: { user: @user }%>
<%= render partial: "partial/new_books", locals: { book: @book } %>
</div>
<div class='col-md-8 offset-md-1'>
<h2>Group Detail</h2>
<table class='table table-hover table-inverse'>
<thead>
<tr>
<th></th>
<th>グループ名</th>
<th>紹介文</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<% if @group.group_image.attached? %>
<%= image_tag @group.group_image,size:"50x50" %>
<% else %>
<%= image_tag 'no_image', size: "50x50" %>
<% end %>
</td>
</td>
<td><%= @group.name %></td>
<td><%= @group.introduction %></td>
<td>
<% if @group.owner_id == current_user.id %>
<%= link_to 'Edit', edit_group_path(@group), class: "btn btn-sm btn-success" %>
<% end %>
</td>
</tr>
</tbody>
</table>
</div>
🌱参考にさせていただいた記事
Discussion