この記事は Money Forward Advent Calendar 2018 15日目の記事です。
こんにちは、マネーフォワードでマネーフォワードクラウドの開発をしている @kamille です。
今年の10月に新卒で入社して、今はマネーフォワードクラウドの全体的な課題を解決する横断チームにてRailsエンジニアをしています。
今日はRailsの accepts_nested_attributes_for
メソッドについてお話したいと思います。
はじめに
突然ですが皆さん accepts_nested_attributes_for
は好きですか?僕は複数リリースの同時保存・更新が超お手軽にできて素敵なコンセプトだなと思います。
でもこのメソッドはあまり評判が良くなく、特にRails生みの親のDHHがaccepts_nested_attributes_forを消したいと言っていたり、代わりとなる公式なformオブジェクトのプロジェクトが立ち上がったり(残念ながら途中でなくなってしまったようです :cry: )している等、あまりいいメソッドと思われていないようです。
社内でも accepts_nested_attributes_for
は今後は使わないようにして、既存のコードもリプレイスしていく活動が始まっているので accepts_nested_attributes_for
を使わずに、 FormObject を使って複数リリースの同時保存を行うコードを書いてみました。
Model
データ構造としては、Companyに紐づく、 Employee
, President
があるとします。
それぞれ has_many
と has_one
の関係で Company
リソースと紐付いていて、Companyを登録する時に2つの子リソースも同時に登録したいものとします。
class Company has_many :employees has_one :president end class Employee belongs_to :company end class President belongs_to :company end
ControllerとView
FormObjectのインスタンスをviewに渡して、form_withの引数にその変数を渡しました。
ここはモデルのインスタンスを渡している普段の使い方がフォームのインスタンスになっただけです。(フォームのインスタンスが form_with
の引数に使えるのは ActiveModel::Model
というAPIをFormObject内でインクルードしているからです)
class RegistrationsController def new @company_registration_form = CompanyRegistrationForm.new end def create @company_registration_form = CompanyRegistrationForm.new(company_registration_form_params) if @company_registration_form.save # 成功 else # 失敗 end end ... private def company_registration_form_params params.require(:company_registration_form_params).permit( name:, address:, employees_attributes: [:first_name, :last_name, :job_type], president_attributes: [:first_name, :last_name] ) end end
Viewでは fields_for
を使ってEmployeeとPresidentリソースも操作できるようにしています。
<%= form_with @company_registration_form do |form| %> First name: <%= form.text_field :first_name %> Last name : <%= form.text_field :last_name %> <%= form.fields_for :employees do |employee_fields| %> First name: <%= employee_fields.text_field :first_name %> Last name : <%= employee_fields.text_field :last_name %> job_type : <%= employee_fields.text_field :job_type %> <% end %> <%= form.fields_for :president do |president_fields| %> First name: <%= president_fields.text_field :first_name %> Last name : <%= president_fields.text_field :last_name %> <% end %> <%= form.submit %> <% end %>
FormObject
ActiveModel::Model
をインクルードして、このクラスのインスタンスを form_with
の引数に使えるようにしたり、このFormObjectを使用するコンテキスト時だけ適用したいバリデーションなどを追加することができます。モデルのバリデーションだとすべてのコンテキストでバリデーションが動いてしまうのでこれは便利です。
class CompanyRegistrationForm include ActiveModel::Model concerning :CompanyBuilder do def company @company ||= Company.new end end concerning :EmployeesBuilder do attr_reader :employees_attributes def employees @employees_attributes ||= Employee.new end def employees_attributes=(attributes) @employees_attributes = Employee.new(attributes) end end concerning :PresidentBuilder do attr_reader :president_attributes def president @president_attributes ||= president.new end def employees_attributes=(attributes) @president_attributes = President.new(attributes) end end attr_accessor :name, :address validate :validate_something def save # バリデーションに引っかかる場合は以降の処理には行かせずfalseをコントローラーに返します return false if invalid? company.assign_attributes(company_params) build_asscociations if company.save true else false end end private def company_params { name: name, address: address } end def build_asscociations company.employees << employees company.president = president end def validate_something # Do something end end
最後に
accepts_nested_attributes_for
を使わない方法はいかがでしたでしょうか。
明日は jjjjjiiiiinnnnn さんが、「価値とUIについて」の記事を公開します。 価値って聞くとマネーフォワードっぽいですね! 笑 それでは!
マネーフォワードでは、エンジニアを募集しています。 ご応募お待ちしています。
【採用サイト】 ■マネーフォワード採用サイト ■Wantedly | マネーフォワード
【マネーフォワードのプロダクト】 ■自動家計簿・資産管理サービス『マネーフォワード ME』 iPhone,iPad Android
■「しら」ずにお金が「たま」る 人生を楽しむ貯金アプリ『しらたま』 iPhone,iPad
■おトクが飛び出すクーポンアプリ『tock pop トックポップ』
■金融商品の比較・申し込みサイト『Money Forward Mall』
■ビジネス向けクラウドサービス『マネーフォワードクラウドシリーズ』 ・バックオフィス業務を効率化『マネーフォワードクラウド』 ・会計ソフト『マネーフォワードクラウド会計』 ・確定申告ソフト『マネーフォワードクラウド確定申告』 ・請求書管理ソフト『マネーフォワードクラウド請求書』 ・給与計算ソフト『マネーフォワードクラウド給与』 ・経費精算ソフト『マネーフォワードクラウド経費』 ・マイナンバー管理ソフト『マネーフォワードクラウドマイナンバー』 ・資金調達サービス『マネーフォワードクラウド資金調達』