#概要
チェックボックスを複数選択し、その情報を配列で受け取って、中間テーブルの関連付けを行います。
E-R図
※今回説明で使用するアプリとカラム数があっていませんが、中間テーブルの使い方として掲載します。
事前準備
ToDo系アプリ
を想定して進めます。
画像のようなタスクを新規作成する画面を用意しました。
今回の話で使用するのは、Labelの複数のチェックボックスから値を取ってくる
方法です。
view
<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
class Task < ApplicationRecord
has_many :labelings, dependent: :destroy
has_many :labels, through: :labelings
end
class Labeling < ApplicationRecord
belongs_to :task
belongs_to :label
end
class Label < ApplicationRecord
has_many :labelings, dependent: :destroy
end
controller
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: []
複数チェックボックスの値を配列で渡すことを許可しています。
タスク新規作成時に、ラベルを複数つける処理の動き
もう一度タスク新規投稿の画面を貼ります。
Labelの開発
・DB操作
・次回MTGまで
にチェックを入れた状態で登録ボタンを押します。
この3つのチェックボックスの値を取得します。
まずparams
には次のような値が送られます
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で値を確認します。
def create
@task = Task.new(task_params)
binding.pry
if @task.save
redirect_to root_path, notice: '新しいタスクを作成しました'
else
render :new
end
end
# 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>]
無事1度のcreateアクションで、中間テーブルに3個保存されました!
タスク編集時に、複数のラベルを変更した時の、中間テーブルの動き
ここまで新規作成時の中間テーブルの関連付けの説明をしました。
では、更新する時はどうやるの?
外すラベルのid調べてテーブルからdeleteしてinsertするの?といった疑問が浮かぶと思います。
実はdeleteとかはいい感じに勝手にやってくれます!
実際の動きを確認してみました
新規作成時に開発
・DB操作
・次回MTGまで
にチェックを入れてます。
viewにchecked: @task.labels.find_by(id: label.id).present?
を記載しているので、デフォルトでチェックが入っています
DB操作
・次回MTGまで
のチェックボックスを外し、
新たに検討中
にチェックしました。
更新するボタンを押します。
(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されています。