この記事は 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』
■ビジネス向けクラウドサービス『マネーフォワードクラウドシリーズ』 ・バックオフィス業務を効率化『マネーフォワードクラウド』 ・会計ソフト『マネーフォワードクラウド会計』 ・確定申告ソフト『マネーフォワードクラウド確定申告』 ・請求書管理ソフト『マネーフォワードクラウド請求書』 ・給与計算ソフト『マネーフォワードクラウド給与』 ・経費精算ソフト『マネーフォワードクラウド経費』 ・マイナンバー管理ソフト『マネーフォワードクラウドマイナンバー』 ・資金調達サービス『マネーフォワードクラウド資金調達』