アラフォーがお金持ちになるためエンジニア目指すブログ

お金も根性も学歴もないアラフォーまきのがエンジニアになってお金持ち目指すよ!

【Railsメモ】form_withでparamsが入れ子になる

どうも、アラフォーまきのです。
form_withの書き方と、入れ子になるわけがすぐわからなくなっちゃうから、またメモ!

メモにしちゃ長いw

form_withの基本の書き方

<%= form_with(model:①, url: ②, method: ③ local: true④) do |form| %>

①関連するモデルがあるならモデルを指定する

関連するモデルってのは、これから入力フォームやらプルダウンやらを書いていくわけだけども、それらのフォームにユーザーが入力した内容を、DBで管理するかどうかってこと。

DBで管理するなら、そのためのモデルがあって、そのためのテーブルがあるはず。
だからそのモデル名を書いてあげる。

検索フォームの場合は、別に入力した検索ワードを保存して何かしようってことはないので、モデルがないことが多いようだ。

ブログやTwitterのように「投稿する系」は、そのデーターをDBへ保存しないといけないから、そのためのモデル=関連するモデルがあるってことになる。


②これから書く入力フォームに入る内容を飛ばす先のURL

このform_withが書かれているのは、どこかしらのnew.html.erbやedit.html.erbである場合がほとんど。

そのデーターを送る先は、new.html.erbならcreateアクション、edit.html.erbならupdateアクション。

それぞれのURLを指定してあげればOK。
URLはroutes.rbや、コンソールでrails routesしてみてあげる。

【routes.rb】

 get 'faqs'          => 'faqs#index'
  get 'faqs/new'      => 'faqs#new'
  get 'faqs/:id'      => 'faqs#show'
  get 'faqs/:id/edit' => 'faqs#edit'
  post 'faqs'         => 'faqs#create'  #ここら辺とか
  patch 'faqs/:id'    => 'faqs#update'  #ここら辺とか
  delete 'faqs/:id'   => 'faqs#destroy'

【コンソールでrails routes】

Prefix  Verb    URI Pattern          Controller#Action
faqs   GET    /faqs(.:format)            faqs#index
faqs_new GET    /faqs/new(.:format)       faqs#new
         GET    /faqs/:id(.:format)       faqs#show
         GET    /faqs/:id/edit(.:format)  faqs#edit
         POST   /faqs(.:format)           faqs#create   #ここら辺とか
         PATCH  /faqs/:id(.:format)       faqs#update   #ここら辺とか
         DELETE /faqs/:id(.:format)       faqs#destroy


③リクエストメソッドがGETじゃないなら明示的に記述する

もともとGETの場合もmetod: :getっていう記述があるんだけれども、省略可能になってる。

GET以外の場合は「GETじゃありませんよー」とはっきりRailsに伝えてやる必要があるので省略しない。

さっきの②の話で、createかupdateがほとんどだよねって話な訳だったのでみてみると、
createならばリクエストメソッドはPOST
updateならばリクエストメソッドはPATCHかPUT
が該当するリクエストメソッドになる。

updateの場合、現時点でのRailsではPATCH推奨らしいので、routesはPATCHにしてみた。もちろん今の段階ではPUTでもおk。


④local: ture

Ajaxについてまだ勉強してないので細かいことはわからなかったけど、
form_withはリモートフォームが基本になったとのこと。

そもそもリモートフォームってなんだよ、Ajaxってなんだよ状態なのだけど
「今のまきのの場合は local: ture でおk」とのことw

詳しくはこの辺とか
qiita.com

本題の入れ子になるやつ

まずはhtml.erbで書いたコードがこちら。

<%= form_with(model: @work,
url: {controller: :work, action: :create, user_id: @user.id},
method: :post,
local: true) do |form| %>

<h3>制作物タイトル:<%= form.text_field :title %></h3>
<h3>画像やテキストファイル1<%= form.file_field :images, multipart: true %></h3>
<h3>コメント:<%= form.text_area :comment %></h3>

こんな感じで、workモデルのworkテーブルに、
制作物タイトル・画像ファイル・コメントを記録する入力フォームを書いた。

モデルが@wrokでいい理由がまだわかってないんだけれども、
どこをみてもだいたいこういう書き方だったので、
とりあえずコントローラのnewアクションで@work = Work.newをかいてうまくいっている状態;

あと、URLの部分にuser_id: @user_idとある通り、
このworkモデルさんは、userモデルさんと紐付けがされている状態。

で、HTMLでこのソースコードをみてみるとこうなってる。

<h3>制作物タイトル:<input type="text" name="work[title]" id="work_title" /></h3>
<h3>画像やテキストファイル1<input multipart="true" type="file" name="work[images]" id="work_images" /></h3>
<h3>コメント:<textarea name="work[comment]" id="work_comment">
</textarea></h3>

制作物タイトルのname="work[title]" id="work_title"とか
画像やテキストファイルのtype="file" name="work[images]" id="work_images"とか
コメントのname="work[comment]" id="work_comment"ってなってる箇所。

これが入れ子になってるって言ってる箇所。

これらの値をcreateなりupdateが受け取れるように、
work_controllerのアクションへコードを書くとこうなる。

work = Work.new(
     title: params[:work][:title],
     images: params[:work][:images],
     comment: params[:work][:comment],
     user_id: params[:user_id],
     )
    work.save!
    redirect_to(飛び先URL)

さっき入れ子になってるって言った部分、paramsで受け取る時に[:work]がついてますよね、user_id以外は。

最初にform_withを書いた時に、model: @work って指定したから。

ちなみにsaveではなくsave!になっているのは、
DBへデーターが入る時にエラーがあったら、ちゃんとそこで止まってもうため。

たまに、ちゃんと画面はリダイレクトしたのに、DBにはデーターが入ってないことがあるけれど、そういうのを防ぐ。

save!とかcreate!とかにしておくと、ちゃんとエラーになって止まってくれる。

さあ、そしてしして。
もし最初の段階でmodel: @workを書かなかったらどうなる?
create時はあんまり困らないです。

work = Work.new(
     title: params[:title],
     images: params[:images],
     comment: params[:comment],
     user_id: params[:user_id],
     )
    work.save!
    redirect_to(飛び先URL)

これでちゃんと受け取ってくれる。

ただ、editの場合はちょっと不便。
model: @workを書いていないってことは
関連するモデルがないってこと。

「データーを保存しているモデル(テーブル)はありませんよ」状態。

だから、編集画面のhtml.erbを開いた時、
createで入力した内容が反映されておらず、
まっしろけの入力フォームが表示されちゃう。

大概編集画面って、もともと入ってた内容をフォームに入れて表示してあげるじゃないですか、こんな感じ↓で。

f:id:MmRevorution:20181221180904p:plain

モデルの指定がないと、これができない。

フォームに入力されたことや、プルダウンで選択された内容を
管理してるDBがあるよ!
ってことと、
編集画面では、編集がしやすいように元の入力内容を見せて欲しいよ!
ってのをRailsに知らせるために、最初に@modelを指定する。

管理してるDBあるよ!ってモデルを指定したから、
入力フォームの内容の受け取りは入れ子になる。

入れ子になるから、データーを受け取る側ででもtitle = params[:work][:title]のような書き方になる。

user_idが入れ子になっていないのは、workのためだけのuser_idは存在しないから。
このuser_idはuserモデルの、usersテーブルのプライマリキー(主キー)。

workモデルのためだけに、わざわざ割り振っているuser_idがあったとしたら、その時はuser_idではなくてwork_user_idとかにして、今あるuser_idとは区別する必要がある。

ちなみに、もしこのworkモデルが、userだけではなくて他のモデルとも紐づいていて、そのプライマリキーが必要な場合も、入れ子にならずparams: [:hoge_id]でとれる。