rails グループ機能の実装

グループ機能の実装について書きます。
基本自分がやってきた内容の備忘録なので
内容が足りないところがありますが、御愛敬で


# 目次

1.コントローラ、モデルを作成する
2.ルーティングを設定する
3.グループに関するビューを作成
4.グループ作成機能を実装する
5.ルーティングを変更し、作成したグループをgroups#indexのサイドバーに表示する
6.グループ編集機能を実装する

## 1.コントローラ、モデルを作成


### 必要ファイルの作成
まず三つのコマンドを行い。作成する

```bash:ターミナル
$ rails g controller groups
$ rails g model group
$ rails g model group_user
```

### モデルからのデータベースの作成

```ruby:groupモデルのマイグレーションファイル
class CreateGroups < ActiveRecord::Migration[5.2]
  def change
    create_table :groups do |t|
      t.string :name, null: false
      t.index :name, unique: true
      t.timestamps
    end
  end
end
```


```ruby:group_userモデルのマイグレーションファイル
class CreateGroupUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :group_users do |t|
      t.references :group, foreign_key: true
      t.references :user, foreign_key: true
      t.timestamps
    end
  end
end
```

```bash:ターミナル
$ rails db:migrate
```


### アソシエーションの定義

```ruby:app/models/group.rb
class Group < ApplicationRecord
  has_many :group_users
  has_many :users, through: :group_users
  validates :name, presence: true, uniqueness: true
end
```

```ruby:/models/group_user.rb
class GroupUser < ApplicationRecord
  belongs_to :group
  belongs_to :user
end
```

```ruby:/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable,  :validatable

  has_many :group_users
  has_many :groups, through: :group_users
end
```

## 2.ルーティングを設定する

グループに置いて下記を行う。

new:新規作成画面を表示する
create:新規作成されたグループを保存する
edit:編集画面を表示する
update:編集されたグループを更新する

```ruby:routes.rb
Rails.application.routes.draw do
  devise_for :users
  root 'messages#index'
  resources :users, only: [:index, :edit, :update]
  resources :groups, only: [:new, :create, :edit, :update] # ここで設定する。
end
```

## 3. グループに関するビューを作成

## 4. グループ作成機能を実装する

```ruby:groups_controller.rb
class GroupsController < ApplicationController
  def new
    @group = Group.new
    @group.users << current_user
  end

  def create
    @group = Group.new(group_params)
    if @group.save
      redirect_to root_path, notice: 'グループを作成しました'
    else
      render :new
    end
  end

  private
  def group_params
    params.require(:group).permit(:name, user_ids: [] )
  end
end
```

エラーメッセージの表示

```haml:app/views/groups/new.html.haml
.chat-group-form
  %h1 新規チャットグループ
# 〜省略〜
```

```haml:groups/new.html.haml
.chat-group-form
  %h1 新規チャットグループ
  = form_for @group do |f|
    - if @group.errors.any?
      .chat-group-form__errors
        %h2= "#{@group.errors.full_messages.count}件のエラーが発生しました。"
        %ul
          - @group.errors.full_messages.each do |message|
            %li= message
    .chat-group-form__field
      .chat-group-form__field--left
        = f.label :name, "グループ名", class: 'chat-group-form__label'
      .chat-group-form__field--right
        = f.text_field :name, class: 'chat__group_name chat-group-form__input', placeholder: 'グループ名を入力してください'
    .chat-group-form__field.clearfix
      / この部分はインクリメンタルサーチ（ユーザー追加の非同期化)のときに使用します
    .chat-group-form__field.clearfix
      .chat-group-form__field--left
        = f.label "チャットメンバー", class: "chat-group-form__label"
      .chat-group-form__field--right
        / グループ作成機能の追加時はここにcollection_check_boxesの記述を入れてください
        = f.collection_check_boxes :user_ids, User.all, :id, :name
        / この部分はインクリメンタルサーチ（ユーザー追加の非同期化)のときにも使用します
    .chat-group-form__field.clearfix
      .chat-group-form__field--left
      .chat-group-form__field--right
        = f.submit class: 'chat-group-form__action-btn'
```

## 5. ルーティングを変更し、作成したグループをgroups#indexのサイドバーに表示する

ルーティングの編集
これまでに指定していたmessages#indexへのルーティングを外し
代わりにroot 'groups#index'を指定します。

```ruby:routes.rb
Rails.application.routes.draw do
  devise_for :users
  root 'groups#index'
  resources :users, only: [:index, :edit, :update]
  resources :groups, only: [:new, :create, :edit, :update] 
end
```

```ruby:groups_controller.rb
class GroupsController < ApplicationController
  def index # ここを追加
  end

  def new
    @group = Group.new
    @group.users << current_user
  end

  def create
    @group = Group.new(group_params)
    if @group.save
      redirect_to root_path, notice: 'グループを作成しました'
    else
      render :new
    end
  end

  private
  def group_params
    params.require(:group).permit(:name, user_ids: [] )
  end
end
```

```haml:app/views/groups/index.html.haml
.wrapper
  = render 'shared/side_bar'
```

```haml:views/shared/_side_bar.html.haml
.side-bar
  .header
    %h3.header__name
      = current_user.name
    %ul.header__lists
      %li.list
        = link_to new_group_path do
          = icon('fas', 'pen-square', class: 'icon')
      %li.list
        = link_to edit_user_path(current_user) do
          = icon('fas', 'cog', class: 'icon')
  / ここから下を編集
  .groups 
    - current_user.groups.each do |group|
      .group
        = link_to '#' do
          .group__name
            = group.name
          .group__message
            メッセージはまだありません。
```

## 6,グループ編集機能の実装

```ruby:groups_controller.rb
class GroupsController < ApplicationController
  before_action :set_group, only: [:edit, :update] # ここ1

#〜省略〜

  def update # ここ2
    if @group.update(group_params)
      redirect_to root_path, notice: 'グループを更新しました'
    else
      render :edit
    end
  end

  private

  def group_params
    params.require(:group).permit(:name, { :user_ids => [] })
  end

  def set_group # ここ3
    @group = Group.find(params[:id])
  end
end
```

グループの作成と編集画面のフォームを共通化

```haml:views/groups/_form.html.haml
= form_for group do |f|
  - if group.errors.any?
    .chat-group-form__errors
      %h2= "#{group.errors.full_messages.count}件のエラーが発生しました。"
      %ul
        - group.errors.full_messages.each do |message|
          %li= message
  .chat-group-form__field
    .chat-group-form__field--left
      = f.label :name, class: 'chat-group-form__label'
    .chat-group-form__field--right
      = f.text_field :name, class: 'chat__group_name chat-group-form__input', placeholder: 'グループ名を入力してください'
  .chat-group-form__field.clearfix
    / この部分はインクリメンタルサーチ（ユーザー追加の非同期化のときに使用します
  .chat-group-form__field.clearfix
    .chat-group-form__field--left
      %label.chat-group-form__label{:for => "chat_group_チャットメンバー"} チャットメンバー
    .chat-group-form__field--right
      / グループ作成機能の追加時はここにcollection_check_boxesの記述を入れてください
      = f.collection_check_boxes :user_ids, User.all, :id, :name
      / この部分はインクリメンタルサーチ（ユーザー追加の非同期化のときに使用します
  .chat-group-form__field.clearfix
    .chat-group-form__field--left
    .chat-group-form__field--right
      = f.submit class: 'chat-group-form__action-btn'
```

```haml:groups/edit.html.haml
.chat-group-form
  %h1 チャットグループ編集
  = render partial: 'form', locals: { group: @group }
```

```haml:groups/new.html.haml
.chat-group-form
  %h1 新規チャットグループ
  = render partial: 'form', locals: { group: @group }
```