has_manyのidsを使って、簡単に中間テーブルの関連付けを行う

#概要
チェックボックスを複数選択し、その情報を配列で受け取って、中間テーブルの関連付けを行います。

## E-R図
![スクリーンショット 2019-07-07 22.57.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398832/51eba829-15e3-4781-44d7-867f0dc4bd1a.png)
※今回説明で使用するアプリとカラム数があっていませんが、中間テーブルの使い方として掲載します。

## 事前準備
`ToDo系アプリ`を想定して進めます。
画像のようなタスクを新規作成する画面を用意しました。
今回の話で使用するのは、`Labelの複数のチェックボックスから値を取ってくる`方法です。
![スクリーンショット 2019-07-07 23.13.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398832/3ae92a3c-2b9e-7c0b-f402-f3c646977a30.png)


### view
```erb:app/views/_form.html.erb
<tr>
  <th>Label</th>
  <td>
    <% Label.all.each do |label| %>
      <%= form.check_box :label_ids, { multiple: true, checked: @task.labels.find_by(id: label.id).present?, include_hidden: false }, label[:id] %>
      <label class='badge badge-secondary'><%= label.name %></label>
    <% end %>
  </td>
</tr>
```
`form.check_box :label_ids`と記述すると、paramsの値として、`"label_ids"=>["7", "8", "10"]`このような形でハッシュのキーを持つ値を送れます。
`multiple`オプションを使用することで、複数のチェックボックスのパラメータを配列形式で送れます。
`checked: @task.labels.find_by(id: label.id).present?`これはeditで使用することを想定しており、`編集するタスク`に紐付いている`ラベル`にチェックを付けるということをしています。
`include_hidden: false`チェックしていない項目については、パラメータを送らないオプションです。

### model

```ruby:app/models/task.rb
class Task < ApplicationRecord
  has_many :labelings, dependent: :destroy
  has_many :labels, through: :labelings
end
```

```ruby:app/models/labeling.rb
class Labeling < ApplicationRecord
  belongs_to :task
  belongs_to :label
end
```

```ruby:app/models/label.rb
class Label < ApplicationRecord
  has_many :labelings, dependent: :destroy
end
```

### controller

```ruby:app/controllers/tasks_controller.rb
  def create
    @task = Task.new(task_params)
    if @task.save
      redirect_to root_path, notice: '新しいタスクを作成しました'
    else
      render :new
    end
  end

 #省略

  private

  def task_params
    params.require(:task).permit(:subject, :content, :expired_at, :state, :priority, :user_id, label_ids: [])
  end
```
`label_ids: []`複数チェックボックスの値を配列で渡すことを許可しています。

## タスク新規作成時に、ラベルを複数つける処理の動き
もう一度タスク新規投稿の画面を貼ります。
![スクリーンショット 2019-07-07 23.13.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398832/fefa9eb2-fa58-223d-47c6-ed6642b9c6e5.png)
Labelの`開発`・`DB操作`・`次回MTGまで`にチェックを入れた状態で登録ボタンを押します。
この３つのチェックボックスの値を取得します。

まず`params`には次のような値が送られます

```ruby
Parameters: { "utf8"=>"✓",
              "authenticity_token"=>"WoRUP+q21xCR760x9L3Y9lyLeQ+THkS9YOGISfGRyDRg0xaI1D2BNnRQLTuaHANpw+NCohg06QtB5U4RxruO5g==", 
              "task"=>{ "subject"=>"タスク新規作成", 
                        "content"=>"ラベルを複数登録", 
                        "expired_at(1i)"=>"2019", 
                        "expired_at(2i)"=>"7", 
                        "expired_at(3i)"=>"8", 
                        "expired_at(4i)"=>"10", 
                        "expired_at(5i)"=>"00", 
                        "user_id"=>"87", 
                        "state"=>"着手中", 
                        "priority"=>"high", 
                        "label_ids"=>["7", "8", "10"]}, 
              "commit"=>"登録する" }
```
次に、controllerのcreateアクション時にbinding.pryで値を確認します。

```ruby
  def create
    @task = Task.new(task_params)
    binding.pry
    if @task.save
      redirect_to root_path, notice: '新しいタスクを作成しました'
    else
      render :new
    end
  end
```

```ruby
# idsだけ確認
pry(#<TasksController>)> @task.label_ids
=> [7, 8, 10]

# newメソッドで作られたlabelsを確認
pry(#<TasksController>)> @task.labels
=> [#<Label:0x00007f92cfe32628 id: 7, name: "開発", created_at: Fri, 05 Jul 2019 15:28:18 JST +09:00, updated_at: Sun, 07 Jul 2019 23:10:17 JST +09:00>,
 #<Label:0x00007f92cfe32470 id: 8, name: "DB操作", created_at: Fri, 05 Jul 2019 15:28:18 JST +09:00, updated_at: Sun, 07 Jul 2019 23:10:24 JST +09:00>,
 #<Label:0x00007f92cfe322e0 id: 10, name: "次回MTGまで", created_at: Fri, 05 Jul 2019 15:28:18 JST +09:00, updated_at: Sun, 07 Jul 2019 23:11:06 JST +09:00>]

pry(#<TasksController>)> @task.save
   (0.9ms)  BEGIN
  ↳ (pry):5
  CACHE User Load (0.0ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 87], ["LIMIT", 1]]
  ↳ (pry):5
  Task Create (33.3ms)  INSERT INTO "tasks" ("subject", "content", "created_at", "updated_at", "expired_at", "state", "priority", "user_id") VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING "id"  [["subject", "タスク新規作成"], ["content", "ラベルを複数登録"], ["created_at", "2019-07-07 23:59:15.509944"], ["updated_at", "2019-07-07 23:59:15.509944"], ["expired_at", "2019-07-08 10:00:00"], ["state", "着手中"], ["priority", 0], ["user_id", 87]]
  ↳ (pry):5
  Labeling Create (0.9ms)  INSERT INTO "labelings" ("task_id", "label_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["task_id", 212449], ["label_id", 7], ["created_at", "2019-07-07 23:59:15.553820"], ["updated_at", "2019-07-07 23:59:15.553820"]]
  ↳ (pry):5
  Labeling Create (0.3ms)  INSERT INTO "labelings" ("task_id", "label_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["task_id", 212449], ["label_id", 8], ["created_at", "2019-07-07 23:59:15.556142"], ["updated_at", "2019-07-07 23:59:15.556142"]]
  ↳ (pry):5
  Labeling Create (0.2ms)  INSERT INTO "labelings" ("task_id", "label_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["task_id", 212449], ["label_id", 10], ["created_at", "2019-07-07 23:59:15.557177"], ["updated_at", "2019-07-07 23:59:15.557177"]]
  ↳ (pry):5
   (0.8ms)  COMMIT
  ↳ (pry):5
=> true

pry(#<TasksController>)> @task.id
=> 212449
pry(#<TasksController>)> Labeling.where(task_id: 212449)
  Labeling Load (0.2ms)  SELECT "labelings".* FROM "labelings" WHERE "labelings"."task_id" = $1  [["task_id", 212449]]
  ↳ app/controllers/tasks_controller.rb:38
=> [#<Labeling:0x00007f92d142e9e0
  id: 11,
  task_id: 212449,
  label_id: 7,
  created_at: Sun, 07 Jul 2019 23:59:15 JST +09:00,
  updated_at: Sun, 07 Jul 2019 23:59:15 JST +09:00>,
 #<Labeling:0x00007f92d142e8a0
  id: 12,
  task_id: 212449,
  label_id: 8,
  created_at: Sun, 07 Jul 2019 23:59:15 JST +09:00,
  updated_at: Sun, 07 Jul 2019 23:59:15 JST +09:00>,
 #<Labeling:0x00007f92d142e760
  id: 13,
  task_id: 212449,
  label_id: 10,
  created_at: Sun, 07 Jul 2019 23:59:15 JST +09:00,
  updated_at: Sun, 07 Jul 2019 23:59:15 JST +09:00>]
```
無事１度のcreateアクションで、中間テーブルに３個保存されました！

## タスク編集時に、複数のラベルを変更した時の、中間テーブルの動き

ここまで新規作成時の中間テーブルの関連付けの説明をしました。
では、更新する時はどうやるの？
外すラベルのid調べてテーブルからdeleteしてinsertするの？といった疑問が浮かぶと思います。
実はdeleteとかはいい感じに勝手にやってくれます！
実際の動きを確認してみました

![スクリーンショット 2019-07-08 0.07.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398832/c0f17ba5-d418-bf4a-be9e-fb1734353f51.png)
新規作成時に`開発`・`DB操作`・`次回MTGまで`にチェックを入れてます。
viewに`checked: @task.labels.find_by(id: label.id).present?`を記載しているので、デフォルトでチェックが入っています

![スクリーンショット 2019-07-08 0.07.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398832/01d827d2-ab81-501d-fea9-e7c69341be33.png)
`DB操作`・`次回MTGまで`のチェックボックスを外し、
新たに`検討中`にチェックしました。
更新するボタンを押します。

```ruby
   (0.2ms)  BEGIN
  ↳ app/controllers/tasks_controller.rb:52
  Label Load (0.3ms)  SELECT "labels".* FROM "labels" WHERE "labels"."id" IN ($1, $2)  [["id", 7], ["id", 9]]
  ↳ app/controllers/tasks_controller.rb:52
  Labeling Destroy (0.3ms)  DELETE FROM "labelings" WHERE "labelings"."task_id" = $1 AND "labelings"."label_id" IN ($2, $3)  [["task_id", 212449], ["label_id", 8], ["label_id", 10]]
  ↳ app/controllers/tasks_controller.rb:52
  Labeling Create (0.4ms)  INSERT INTO "labelings" ("task_id", "label_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["task_id", 212449], ["label_id", 9], ["created_at", "2019-07-08 00:08:31.021375"], ["updated_at", "2019-07-08 00:08:31.021375"]]
  ↳ app/controllers/tasks_controller.rb:52
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 87], ["LIMIT", 1]]
  ↳ app/controllers/tasks_controller.rb:52
   (2.1ms)  COMMIT
  ↳ app/controllers/tasks_controller.rb:52
```
すると、中間テーブルからチェックボックスを外したlabel_idがDestroyされた後にCreateされています。