[Rails]多対多のモデルの実装、コントローラー、ビューの書き方

# はじめに
Railsで多対多のモデルを作り、コントローラー・ビューを実装しました。過去にもやったことがあったので、できるかなーと思ったら、かなり迷ってしまったので、手順をメモしておきます。

## データベース設計

今回のテーブルはこんな感じ。作品（works）は複数のカテゴリー（categories）をもち、それぞれのカテゴリーにも複数の作品が属しています。

### worksテーブル
|column|type|
|----|----|
|id|integer|
|name|string|
|description|string|
|...|...|

### categoriesテーブル
|column|type|
|----|----|
|id|integer|
|name|string|

### work＿categoriesテーブル（中間テーブル）
|column|type|
|----|----|
|id|integer|
|category_id|integer|
|work_id|integer|

ところで、現在、特に中間テーブルに命名規則はないようなのですが（※）。２つのテーブル名を連結した名前をつける場合には、__アルファベット順につける__のが慣習のようです。

私は知らずに`work_category`テーブルという名前にしてしまったので、以下の記述でもそのまま書かせていただきます。。。

※ `has_and_belongs_to_many`を使っていた頃には命名規則があったみたいです。今は、極力意味のあるテーブル名にするのがルールということです。

## migrationファイルの記述

中間テーブルのmigrationファイルを作るときには若干ポイントがありました

```
$ rails g model work_category
```

```ruby:migrationファイル
def change
  create_table :work_categories do |t|
    t.references :work, index: true
    t.references :category, index: true, foreign_key: true
    t.timestamps
  end
end
```

この記述で、work_idとcategory_idカラムがそれぞれ生成されます。各項に`index: true`オプションをつけて、検索を高速化。`foreign_key: true`オプションをつけて、categoryテーブルにないカテゴリーが追加されないようにしています。

ただし、`foreign_key: true`をつけると依存関係にある他のテーブルのカラムを自由に編集できなくなるので、今回の練習用のアプリのように、考えながら作る場合は、最後に外部キー制約をつけても良いのかもしれません。。。

▼参考リンク
[外部キーの概要と制約を使うことのメリット・デメリット](https://qiita.com/kamillle/items/5ca2db470f199c1bc3ef)

## modelにアソシエーションを追記
work, category, work_categoryのモデルに、それぞれ以下のようにアソシエーションを追記します。

単数形、複数形の気遣いが必要で、[Railsガイドの多対多のリレーション](https://railsguides.jp/association_basics.html#has-many-through%E9%96%A2%E9%80%A3%E4%BB%98%E3%81%91)の項を参照しました。

```models/category.rb
class Category < ApplicationRecord
  has_many :work_categories #1
  has_many :works, through: :work_categories #2
end
```
`#1`と`#2`はこの順番で書かないとエラーが出ます。以下、同じです。

```models/work.rb
class Work < ApplicationRecord
  has_many :work_categories, dependent: :destroy
  has_many :categories, through: :work_categories
  accepts_nested_attributes_for :work_categories, allow_destroy: true
end
```
```models/work_category.rb
class WorkCategory < ApplicationRecord
  belongs_to :work
  belongs_to :category
end
```
`dependent: :destroy`は、依存関係にあるデータも削除するオプションで、もし、あるworkが削除されたら、それに関連するwork_categoriesテーブルのデータも削除されます。

`accepts_nested_attributes_for`は、関連する項目も含めて一気に更新、削除する際の設定項目です。

`dependent: :destroy`があればいらないのでは？？と思うけれども、カテゴリーの内容が変更されたときに、これがあると便利なのだろうか？？ちょっと検証が必要な部分です。（後日追記するかもしれません）

なお、この辺りに関しては、こちらの記事が大変ためになりました。

▼参照した記事
[railsで多対多な関係を実装する時のポイント（加筆修正するかも）](https://qiita.com/m-shin/items/0487105994d9281221d4#%E6%8A%95%E7%A8%BF%E3%81%AB%E9%96%A2%E3%81%99%E3%82%8B%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E7%94%9F%E6%88%90)

## controllerのwork_paramsの編集

```ruby:controllers/works_controller.rb
def work_params
  params.require(:work).permit(:name, :description,  { category_ids: [] })
end
```

Workを作成（new）・編集(edit・update)するときに、work_categoriesテーブルにデータを入れられるよう、strong parameterに` { category_ids: [] }`でwork_categoriesの配列の入力を許可しています。

## viewでカテゴリー一覧をチェックボックスすで表示し、編集できるようにする
works_controllerのnewとeditで、categoryをチェックボックスで表示し、チェックを入れるとカテゴリーの値が設定・編集できるようにします。

▼作りたいものはこんな感じ

[![Image from Gyazo](https://i.gyazo.com/acada06811ff3b924a7a625fd029a394.png)](https://gyazo.com/acada06811ff3b924a7a625fd029a394)

▼書いたコードはこちら

```ruby:controllers/works_controller.rb
= collection_check_boxes(:work, :category_ids, Category.all, :id, :name ) do |t|
      =t.label { t.check_box + t.text }
```

こちらのコードの意味に関しては、以前書いた記事があるので、拙著ですがこちらをご参照ください。

[【初心者向け】チェックボックスの書き方あれこれ[Ruby][Rails]](https://qiita.com/tanutanu/items/b86c4adc26ae464c71fd)

さて、以上をもって多対多の関係を持ったモデル・コントローラー・ビューの実装が一通り完成しました。いざやり始めると細かい気遣いが色々必要でしたので、また他のテーブルとのリレーションを組むときに、参照していきたいです。

最後まで読んでくださり、ありがとうございました。