form_with .date_select 保存できない パラメータ

 Railsのフォームヘルパーであるform_with であるが、面倒なところがまたあった。

 form_withのメソッドで年月日をセレクトボックスで入力できるようにする.date_select というものがある。これ、便利だけれど、パラメータの保存が面倒だったので備忘録。

 

 form_withの使い方はここのサイトにある通り。引数で様々なオプションを指定することになるので、ここでよく確認しておこう。

 https://pikawaka.com/rails/form_with#form.date_select

 

 さて、今回はプロフィール登録画面で生年月日を入力するフォームを作った。

※ HTMLはhamlで書いています。

      f:id:kc-masui:20200328152351p:plain 

 (Viewファイル)

 .form-group
   = f.label "生年月日"
   = sprintf(f.date_select(:birth_day, prefix:'birth_day', prompt:"--",use_month_numbers:true,
    start_year:Time.now.year, end_year:1900, date_separator:'%s'),'年','月')+'日'

  sprintfというちょっと特殊な書き方をしているのは年月日の区切りを入れるためです。

以下のサイトを参考に

● https://yoshiob.hatenadiary.org/entry/20091105

 

 この記述で送られるパラメータをUsers_ControllerのupdateアクションからUsers_Tableのbirth_dayカラムに保存できるようにしたい。

 このViewのフォームから送られるパラメータは

 

  Parameters: {"utf8"=>"✓", "authenticity_token"=>"ibjRO〜〜i6xZnQ8Txdw==",

"user"=>{"name"=>"ほげ" , "nickname"=>"ほげちゃん", "profile"=>"ほげほげ"},

"birth_day"=>{"birth_day(1i)"=>"2000", "birth_day(2i)"=>"1", "birth_day(3i)"=>"1"},

"commit"=>"登録する", "id"=>"1"}

 

 このような構造になる。注目すべきは三段目。

● 今回はform_withによってUserモデルにアクセスするパラメータを作ったので、入力フォームに記述した内容はParams: { user: { ここに入力内容 }}というように二重のハッシュで格納されるはずである。

 しかしbirth_day の内容はuser:{  }の外に作られる。これの何が問題か、というと、ストロングパラメータを作る際にrequire(:user)をつけるところでエラーになる。

 requireメソッドで二重構造のハッシュを解消しているが、その中にbirth_dayのキーがないので、permitメソッドを適用できなくて、Parameter_permittion_errorみたいなのになる。

● もう一点、birth_dayキーの中身を見てみると、入力した年月日の情報がバラけてそれぞれ"birth_day(1i)"的なキーとバリューで保存されている。このままだとDBのuserテーブルにあるbirth_dayカラムにきちんとまとめて保存されない。(birth_dayカラムのデータ型はDATE型にしている)

 このバラバラになっているデータをひとまとめにする必要がある。

 

 これらの状況を解決していく。備忘録。

 

●コントローラの記述


 def update
  params[:user][:birth_day] = birthday_join …①
   # birth_dayは変数が特殊なので特別に処理
  @user.update(user_params)
 end

 private
 

 def user_params
  params.require(:user).permit(:name,:birth_day,:nickname,:profile)
 
 end

 def birthday_join …②
  date = params[:birth_day]
   # ブランク時のエラー回避のため、ブランクだったら何もしない
  if date["birth_day(1i)"].empty? && date["birth_day(2i)"].empty? && date["birth_day(3i)"].empty?
   return
  end
   # 年月日別々できたものを結合して新しいDate型変数を作って返す
  Date.new(date["birth_day(1i)"].to_i,date["birth_day(2i)"].to_i,date["birth_day(3i)"].to_i)
 end

 

① params[:user][:birth_day] = birthday_join

 この記述でまず右辺のbirthday_joinメソッドを起動させる。返り値をparamsのuserキーの中にbirth_dayキーとして新たに保存する。これでストロングパラメータのrequire問題を解決する。(この辺カラム名との整合性に注意)

 

② birthday_joinメソッドの中身

・ date = params[:birth_day] … paramsの中の:birth_dayハッシュの値を取得

 

・ if date["birth_day(1i)"].empty? && date["birth_day(2i)"].empty? 〜〜

  …入力が空だった時のエラー回避、空のままで値を返す記述

 

・ Date.new(date["birth_day(1i)"].to_i,date["birth_day(2i)"].to_i,date["birth_day(3i)"].to_i)

 …バラけてたデータをto_iメソッドで数値化して、それをDateクラスで改めてDate型のインスタンスとして再構成する。このインスタンスを元のParamsに戻します。

 

 これにて

 Parameters: {"utf8"=>"✓", "authenticity_token"=>"ibjRO〜〜i6xZnQ8Txdw==",

"user"=>{"name"=>"ほげ" , "nickname"=>"ほげちゃん", "profile"=>"ほげほげ",

"birth_day"=>{"2000-1-1"},"commit"=>"登録する", "id"=>"1"}

 

   になって全ての入力内容がuser: {  }のハッシュに入ったので、ストロングパラメータでPermittionがかけられてエラーが起きなくなりました。

 

 

 このように.date_selectはパラメータの値が面倒臭いので気をつけて実装しましょう。

 

 <参考サイト>

 ● https://qiita.com/ozackiee/items/3c027d07cdeb61df6029