Formオブジェクトを使って、ActiveRecord経由でDBにレコードを保存する場合、エラー内容を適切にマージする必要があるよーということの備忘録。

例えば、以下のようなActiveRecordのUserクラスとFormオブジェクトであるUserRegisterFormクラスがあるとする。

この場合、コントローラ側ではこんな感じで呼び出して、内部のロジックは全てUserRegisterFormやUserの中に入れる感じになる。いわゆる Skinny Controller。

View側はこんな感じになる。Formクラスを使えばControllerもViewも同じように書けるので可読性あって良い、というのもメリット。

ここで問題なのが@form.errorsの部分で、このままだとUserRegisterForm#saveで内部的に呼び出されるUser#saveでエラーになった時のエラー内容は返さず、Formクラス内のエラーしか返してくれない。

Userのエラー内容をそのままerrorsにマージしても良いのであれば、以下のようなコードでUserクラスのerrorsをごっそりForm側に持っていくことができる。

が、この方法にも問題があって、Formとモデルに同じバリデーションがあった場合にはバリデーションメッセージが重複する。

例えば、上のようなバリデーションをFormに追加して、email未入力のエラーが発生するとエラー内容が重複する。

なのでerrorsをFormクラスにマージするときはここらへんも考慮してマージする必要がある。

とはいえ、Formに切り出す目的としては “コンテキスト特有のバリデーション、コールバックや外部サービスへの連携(例えばメール通知)をモデルから切り離してメンテナビリティ上げる” ということだと思うので、モデルにも入っているコンテキスト特有ではない共通のバリデーションがFormにも入っていること自体がおかしいので、ここらへんの重複問題は起こり得ないとは思われる。