今回製作したアプリケーションは、今まで作ったアプリケーションとは違い一つのcontrollerのアクションで、二つのmodelに登録・変更を行えるようにすることが最大の難所だったが、以下の4のメソッドを使ったら無事にアプリが作れた
1.accepts_nested_attributes_forメソッド
accepts_nested_attributes_forメソッドとは、親モデルと紐付いている子モデルの値も一緒に保存ができるメソッドです
今回のアプリケーションの場合だと、一つcontroller(controller名:houses_controller)で家の情報を登録する親のmodel(model名:house.rb)とその物件の近くの駅の情報を複数登録できる子のモデル(model名:station.rb)があり、入力された物件情報とその物件の最寄駅の情報を、DBは違いますが、controllerのcreateアクションが働くことで一緒に保存してくれます。
また、今回は、accepts_nested_attributes_forメソッドを以下のように記述しましたが、テーブル名の後にupdate_only: trueとreject_if: :all_blankと記述しましたが、この二つについても説明します
accepts_nested_attributes_for :stations, update_only: true, reject_if: :all_blank
update_only: true
:この部分は、親モデルの更新時に子モデルの値も更新するようになります。つまり、子モデルの新規作成や削除は行われず、既存の子モデルの値のみが更新されます。このオプションを指定しない場合、親モデルの更新時に子モデルの新規作成や削除も行われます。
reject_if: :all_blank
:このオプションを指定すると、何も書いていない空の子モデルの属性を拒否し、無駄な空のレコードを作らないようになります。具体的には、子モデルの値が何も入力されなかった(blank)である場合、その子モデルは無視されます。つまり、その子モデルは親モデルとの関連付けや保存の対象となりません。このオプションを指定しない場合、からの子モデルも親モデルとの関連付けや子モデルのDBへの保存の対象となり、何の値も入っていないレコードが作成されてしまいます。
2.fields_forメソッド
fields_forメソッドは、親モデルと関連付けられた子モデルの入力欄を一つのフォームで表示させるために、viewファイルのnew, edit, もしくはパーシャルファイルに記述します。これにより、親モデルと関連する複数の子モデルに対して、作成・編集することができます。fields_forメソッドはform_withやform_forなどのフォームヘルパーの中で使われます。
例えば、今回のアプリケーションの場合だと、家の情報を登録する親のモデル(model名:house.rb)とその物件の近くの駅の情報を複数登録できる子のモデル(model名:station.rb)があります。物件情報を登録する際にnewアクションで呼び出されたview画面では、物件の情報を入力する欄と最寄駅の情報を入力する欄が一画面でまとめて現れます(※画像1)。それぞれ入力された値はそれぞれのモデルを介して別々のhousesテーブルとstationsテーブルに保存されます。
3.buildメソッド
buildメソッドとは、関連付けられたモデルのインスタンスを生成し、親モデルのインスタンスに自動的に紐づけるメソッドです。アソシエーションを利用してインスタンスを作成するときに便利です。
今回の例で言うとnewアクションとeditアクションにインスタンス変数.テーブル名.build(@house.stations.build)と記載してあげることかつ、viewファイルにfields_forメソッドを定義してあげることで、簡単に駅情報の入力画面呼び出すことができます。さらに、newアクションにも書いてある、timesメソッドで使いくり返し処理の値を決めることで、fields_forメソッド直下に作った入力欄を返し処理の値分、出力させることができます。今回は、2.timesとしているため、(※画像1)のように駅情報を入力する画面を2つ出しています。(※画像1)(※画像2)
4.ストロングパラメータでのattributesメソッド
ストロングパラメータでのattributesメソッドは今回の場合、親モデルであるHouseと子モデルであるStationの属性を同時に保存するためのパラメーターです。これにより、new画面などで情報を登録した際に親である物件の情報と子である駅の情報の2つにストロングパラメーターが適用されます。
def house_params
params.require(:house).permit(:house_name, :rent, :address, :house_age, :remarks,
stations_attributes: [:near_station_name, :line_name, :walk, :_destroy, :id])
end
上記のコードの_destroyとidですが、_destroyは子モデルのレコードを削除するときに必要です。_destroyを許可することで、親モデルに紐づく子モデルのレコードを特定し、そのレコードをデータベースから削除できます。_destroyがない場合は、レコードの削除ができません。なので今回は、Houseモデルに紐づくStationモデルのレコードを削除するときには、以下のように_destroyを含める必要があります。
idは子モデルのレコードを更新するときに必要です。idを許可することで、親モデルに紐づく子モデルのレコードを特定し、その属性を変更できます。idがない場合は、新しいレコードが作成されます。なので、既存のレコードを更新したい時にはidは必須です。