🧀

Rails グループ作成

2023/05/04に公開

グループ作成機能

グループを作れるようにする流れについてまとめました🏋🏻

要件

  • グループを作成する
  • ユーザーの一覧画面に 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ファイルの編集

20xxxxxx_create_groups.rb
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
20xxxxxx_create_group_users.rb
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が使えるようになる!

🌱わかりやすい記事
https://qiita.com/ryouzi/items/2682e7e8a86fd2b1ae47
🔑外部キーの設定法
http://www.code-magagine.com/?p=12427

忘れずに行う!

rails db:migrate

アソシエーション記述

group_user.rb
class GroupUser < ApplicationRecord
  belongs_to :user
  belongs_to :group
end
group.rb
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
user.rb
class User < ApplicationRecord
  has_many :group_users, dependent: :destroy
end

has_many :groups, through: :group_users
userはgroup_usersを通してgroupsにアクセスできるので、
この記述も必要かな?と思ったのですが、回答を見たところなくても問題なく動作したので消しました。
現時点ではあってもなくても大丈夫っぽいです🧐

🌱データ管理についてわかりやすい記事
https://web-camp.io/magazine/archives/17680

ルーティングの設定

routes.rb
resources :groups, only: [:new, :index, :show, :create, :edit, :update]

コントローラー作成

groups_controller.rb
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 %>

その他のビューページ

edit.html.erb
<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.html.erb
<%= 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 %>
index.html.erb
<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>
_index.html.erb
<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> 
new.html.erb
<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>
show.html.erb
<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>

🌱参考にさせていただいた記事
https://qiita.com/ki_87/items/5add92e34c639fe13ef0

Discussion