はじめに
フォームからネストされた値を渡し、ストロングパラメータとして取得するだけのサンプルコードです。
バージョン
アソシエーション
Product -< Medium
Product >-< Technology
こんな感じで、Productを基準としてそれぞれ一対多・多対多のアソシエーションです。
View
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
= form_with model: @product, do |f|
= f.label :title
= f.text_field :title
= f.label :summary
= f.text_area :summary
/ ① 多対多のアソシエーション
= f.label :technology_ids
= f.select :technology_ids, Technology.order(:id).pluck(:name, :id), { include_blank: 'なし' }, { multiple: true }
/ ② 一対多の子レコードを同時に作成する
- 2.times do |i|
= f.fields_for 'media_attributes[]', Medium.new do |e|
= e.label :title
= e.text_field :title
= e.label :description
= e.text_area :description
= f.submit
|
(コード例ではclassなどは省略しています)
① 多対多のアソシエーション
そもそも、f.label
やf.select
の第一引数には、モデルのゲッターメソッドのシンボルを入れることができます。そして、値がフォームのvalueに入ります。
こんな感じ。
1
2
3
4
5
|
class Product < ApplicationRecord
def hoge
'ほげですよ'
end
end
|
1
2
|
= f.label :hoge
= f.text_area :hoge
|
なので、@product.title
や@product.description
が呼ばれているのと同じように、@product.technology_ids
(関連technologyのidの配列)を呼んでいるようなものです。
このtechnology_ids
は、Productにhas_many technologies
を定義することで、Productのインスタンスメソッドとして使えるようになります。
collection_singular_ids
メソッドは、そのコレクションに含まれるオブジェクトのidを配列にしたものを返します。
@book_ids = @author.book_ids
Active Record の関連付け - Railsガイド
② 一対多の子レコードを同時に作成する
2.times do |i|
の繰り返しの部分は、JSで要素を追加する処理を行うのがいいのかなと思います。(Hotwireでの実装例)
今回は子レコードのattributesを配列で渡すサンプルなので、こういう風にゴリ押しな書き方をしています。
fields_for
については、ドキュメントの説明とコード例が「はい」って感じなので、以下を参照してください。
モデルを固定してフォームを生成
form_for内で異なるモデルを編集できるようになる
fields_for | Railsドキュメント
引数の'media_attributes[]'
は、コントローラに配列を送るための書き方です。media_attributes
という命名はaccepts_nested_attributes_for
に倣っています。
Railsは、フォームのname
要素からparamsを組み立てるので、このように指定します。
Railsは、重複したパラメータ名を無視します。パラメータ名に空の角かっこ[ ]
が含まれている場合、パラメータは配列の中にまとめられます。たとえば、電話番号入力時に、複数の電話番号を入力できるようにしたい場合、フォームに以下を置くことができます。
Action View フォームヘルパー - Railsガイド
Params
フォームにこのように記述し、値を送ります。コントローラで習得したparamsはこんな感じです。
1
2
3
4
5
6
7
8
|
20: def create
21: binding.pry
=> 22: end
[1] pry(#<ProductsController>)> params
=> #<ActionController::Parameters{"authenticity_token"=>"WsUUeJ4TWugMGw5MgYgUZw8d3DdwyzCo03ZuDimySqbmB3c3uMkVlvH_r4oU7BZ0PEuGypb1ntsgFofZ4q9Ozw", "product"=>{"title"=>"ほげ","summary"=>"サービス概要", "technology_ids"=>["", "2", "51", "52"], "media_attributes"=>[{"title"=>"メディア名", "description"=>""},{"title"=>"", "description"=>""}]}, "commit"=>"登録する", "controller"=>"products", "action"=>"create"} permitted: false>
[2] pry(#<ProductsController>)> params[:product]
=> #<ActionController::Parameters {"title"=>"ほげ", "summary"=>"サービス概要", "technology_ids"=>["", "2", "51", "52"], "media_attributes"=>[{"title"=>"メディア名", "description"=>""}, {"title"=>"", "description"=>""}]} permitted: false>
|
params[:product]
を整形するとこんな感じです↓↓
ちゃんとネストされた形でパラメータが形成されていることがわかります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
{
"title"=>"ほげ",
"summary"=>"サービス概要",
"technology_ids"=>["", "2", "51", "52"],
"media_attributes"=>[
{
"title"=>"メディア名",
"description"=>""
}, {
"title"=>"",
"description"=>""
}
]
}
|
そして、これをストロングパラメータで取得する場合は、以下のようになります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
class ProductsController < ApplicationController
# 省略
private
def product_params
params.require(:product).permit(
:title,
:summary,
technology_ids: [],
media_attributes: [:title, :description]
)
end
end
|
1
2
|
[3] pry(#<ProductsController>)> product_params
=> #<ActionController::Parameters {"title"=>"ほげ", "summary"=>"サービス概要", "technology_ids"=>["", "2", "51", "52"], "media_attributes"=>[#<ActionController::Parameters {"title"=>"メディア名", "description"=>""} permitted: true>, #<ActionController::Parameters {"title"=>"", "description"=>""} permitted: true>]} permitted: true>
|
params.permit(:name, { emails: [] },
friends: [ :name,
{ family: [ :name ], hobbies: [] }])
この宣言では、name、emails、friends属性が許可されます。
Action Controller の概要 - Railsガイド
accepts_nested_attributes_for
を使っている場合はこのままProduct.create(product_params)
で関連レコードごと作成できます(今回は配列の2つ目がバリデーションエラーで失敗しますが)。
しかし、accepts_nested_attributes_for
を使わない場合はフォームオブジェクトで値をよしなにする必要があります。
この後の実装に関しては、【Rails】find, updateを備えたフォームオブジェクトに書きました。