woshidan's blog

そんなことよりコードにダイブ。

RailsのFormクラスについて

Railsでバリデーションを書くケースといえばビジネスロジックで場所はModel(ActiveRecord)が多いのではないかと思います。

これは、たとえばRails Tutorialで扱うような、フォームで扱うModelが一つかつ単純な場合には特に迷う必要はありません。RailsのフォームでsubmitされたパラメータをModelに渡してそのままActiveRecordのバリデーションを使うことができます。

しかし、実際のアプリケーションでは、たとえば住所+配送方法など複数のModelをユーザーの利便性を考えたりすると一つのフォームで扱うことはありうるでしょう。

こういう場合、submitされたパラメータを処理するController側がModelに対応するようにパラメータを編集するか、Model側がフォームの中身を知ってパラメータの形式とModelの対応のコードを引き受けるか、ということになりがちです。

個人的には(Webブラウザという)クライアントからくるリクエストパラメータの形を見てビジネスロジックが扱うクラスが処理できる形にして渡すのはどちらかというとコントローラ側の仕事であって、ModelがいちいちViewのことを知るな、と思うんですが、どちらにせよ見通しが悪くなりがちです。

そこで、Railsでは具体的なテーブルと対応しないForm用のモデルクラスを用意して、Modelの属性とフォームで入力するパラメータの構成が綺麗にマッチしない部分を処理させる実装のパターンがあり、それに利用するクラスをform objectというようです。

form objectで嬉しいのは

  • ActiveRecordと同じバリデーションの作法が使える
  • formオブジェクトにActiveRecordのモデルと同様に渡すことができ、バリデーションエラーのメッセージを渡す処理が書きやすい
  • これまでActiveRecordのModelに書いたりControllerに書いたりで置き場所が落ち着かなかったロジックをまとめやすい

あたりだと思います。

具体的なコードは参考元を見るとして、コード上の要点としては

  • ActiveRecordと同じバリデーションを使うために ActiveModel::Model をinclude
  • ActiveRecordと違って対応するスキーマがないので扱うパラメータを明示するために attr_accessor を書く
  • form object は form_for に渡せる(バリデーションエラーを渡したりなど便利)

参考

短いですが、今週の現場からは以上です。