【Rails】form_withからhidden_fieldでデータを送る際のポイント

# はじめに
Rails6.1での予約機能の作成時に、予約を確定する前に確認画面を挟むことにしました。

その際に商品のデータを渡す方法に戸惑ったので、どのように実装したか記録しておこうと思います。

# 結論
今回私がデータをうまく渡せなかったのは、paramsの使い方をきちんと理解できていなかったことが原因でした。

ビューからform_withを使ってコントローラにデータを渡し、送られたデータをコントローラで受け取るためにはparamsを使用します。

paramsはハッシュでデータが格納されるので、キーが必要になります。
私はこちらの認識が抜けていました。

```ruby:
customer = Customer.find_by(email: params[:customer][:email])
```
form_withから@&#8203;customerでmodelを指定して送ったデータを受け取るには、上記のようにキー[:customer]が必要になります。


# createまでitem_idを渡す方法を考える
今回私がやりたかったのは、
商品詳細（show）から予約ボタンを押下
　↓
フォームにデータを入力（new）
　↓
確認画面（cnfirm）
　↓
データの保存（create）
　↓
予約完了画面（thanks）の表示という流れです。

ここでの懸念点としては、確認画面からcreateまでitem_idを渡すにはどうしたらいいのだろうということでした。

ここからは、実際に行った手順に沿ってご説明します。

## 1. hidden_fieldでshow→newへデータを渡してみる

まずは、ビューからコントローラへデータを送るためにhidden_fieldを記述します。

```html:show.html.erb
<%= form_with url: new_pre_order_path, method: :get do |f| %>
  <%= f.hidden_field :item_id, value: @item.id %>
  <%= f.submit 'この商品を予約する', class: "btn btn-info p-2" %>
<% end %>
```

これでnewのビューからconfirmのコントローラへデータを渡せるはずです。
次にnewコントローラにデータを受け取る記述をしました。

```ruby:pre_orders_controller.rb
  def new
    @item =  Item.find(params[:item_id])
    @pre_order = PreOrder.new
  end
```

実際に「この商品を予約する」を押下してターミナルを確認してみます。

```
Parameters: {"item_id"=>"1"}
```

ログを見ると"item_id"=>"1"がありました。
無事、item_idのデータを送れたようです。


## 2. 同じようにnew→confirmへデータを渡す

こちらからもhidden_fieldでitem_idを渡すことにしたのですが、今度はエラー表示になってしまいました…。

ターミナルを確認すると、

```
Parameters: {"authenticity_token"=>"[FILTERED]", "pre_order"=>{"visit_day"=>"5/5", "visit_time"=>"15時", "purpose"=>"a", "note"=>"a", "item_id"=>"1"}, "commit"=>"予約情報を確認する"}
```

先の方を確認すると、"item_id"=>"1"という表示が見られます。
ということは、データ自体は送られているような...？

newでは受け取れるのにconfirmでは失敗してしまう...違いはどこなんだ...と、ここで時間を溶かします。


## 2. paramsについて調べる

詰まったのでいったんparamsについて調べることにします。

https://railsguides.jp/action_controller_overview.html#%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5%E3%81%A8%E9%85%8D%E5%88%97%E3%81%AE%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF

>Railsでは、パラメータをクエリ文字列で受け取ることもPOSTデータで受け取ることもできます。いずれの場合も、コントローラ内ではparamsという名前のハッシュでパラメータにアクセスできます。

調べてすぐ気づきましたが、paramsはハッシュでデータを送っているのでした。
ハッシュのデータを受け取るためには確かキーが必要だったはず...。

改めて先ほどのターミナルのログを確認してみると、

```
Parameters: {"authenticity_token"=>"[FILTERED]", "pre_order"=>{"visit_day"=>"5/5", "visit_time"=>"15時", "purpose"=>"a", "note"=>"a", "item_id"=>"1"}, "commit"=>"予約情報を確認する"}
```

確かに言われてみればこの書き方はハッシュのようです。
だんだん分かってきました。

ということは、データを渡せないのはキーのpre_orderがないからなのでは...？


## 3. キーのpre_orderをparamsに指定してみる

```ruby
@item = Item.find_by(params[:pre_order][:item_id])
```

コントローラで[:pre_order]を追加して再びページを確認してみると...確認画面（confirm）が表示されました！
やはりデータを受け取るためのキーが必要だったみたいです。


# 問題点の整理

show→newでキーが必要なかったのは、そもそもshowからform_withでデータを送る際にmodelを指定していなかったからです。（保存が不要だったため）
そのため、ハッシュではなく単体でitem_idが送られていました。

しかし、newでは保存するためのフォームが必要だったためmodelを指定しています。
modelを指定したことで、データがハッシュの形で格納されたということなのでしょう。

そのため、confirmでデータを受け取るためにはキーの情報も必要だったということになります。

ターミナル上でも、item_idが送られているという状態だけを確認して、どのような形で送られているかに意識を向けられていませんでした。
ここが特に問題でした…反省です…。

# おわりに
なんとか自力で解決はできたのですが、途中でもしかしてpostだからだめなのか？とかルーティングを変えれば良いのでは？とか、少し外れた思考になってしまっていたことで原因究明に時間がかかってしまいました。

paramsの仕組みを知っておくのはもちろん、きちんと意識していればターミナルを確認した時点でデータの送り方が違っていたことに気づけたはずです。
データの扱いや送られたデータの記述形式についても、理解する必要があるなと感じました。

しっかりと基礎を固めることが大切ですね…。