Rails4でFormオブジェクトを作る際に気をつける3つのポイント
ActiveRecord / Rails / Ruby
この記事はRuby on Rails Advent Calendar 2014の2日目です。
1日目は@miyukkiさんの「結局Ruby on RailsとPHPってどっちが優れてるの?」でした。おつかれさまでした。
Formオブジェクトとは
Formオブジェクトはその名の通り入力フォーム用のオブジェクトです。
フォームとモデルがうまく対応しているときはActiveRecordをそのまま使えば良いのですが、 複数モデルを作りたかったりモデルとは違うValidationを行いたかったりする場合にはFormオブジェクトを使うと便利です。
Formオブジェクトのサンプルコードはこんな感じになります。
この例ではBlog::Site
がActiveRecordモデルになっています。
では、以下で気をつけるポイントを紹介します。
form_forにFormオブジェクトを渡してもURLが生成されない
form_for
にActiveRecordオブジェクトを渡してもちゃんとURLが生成されません。
これはform_for
が受け取ったオブジェクトのクラス名を元にURLを生成しているからです。
例えばBlog::Site
オブジェクトを渡せばblog_sites_path
ヘルパーを実行してくれるのですが、Blog::SiteForm
オブジェクトを渡すとblog_site_forms_path
ヘルパーを実行しようとして失敗してしまいます。
なので、次のようにURLを直接指定しましょう。
追記(2014/12/3)
@joker1007さんから「FormオブジェクトのURLの渡し方について」という記事で突っ込みをいただきました。
今回のような場合はpolymorphic_pathを使って
とした方が良さそうです。こうすればurl
, persisted?
メソッドは不要になります。
合わせて
はinclude ActiveModel::Model
に置き換えられるという指摘もいただいたのですが、手元のコードだとcreate
アクション実行時にエラーが出てしまったので、こちらはひとまずこのままにしておきます。原因特定したら別途追記します。← Virtus.model
を使う場合は単純にはActiveModel::Model
はincludeできないようです。
validate_uniqueness_ofが使えない
一意性制約を検証するにはデータベースのUNIQUE制約を利用する必要があります。
ただ、Formオブジェクトからはデータベース制約を直接扱えません。
なので、validate_uniqueness_of
はActiveRecordオブジェクト(今回の例だとBlog::Site
オブジェクト)で実行する必要があります。
具体的にはサンプルコードのようにFormオブジェクトのvalid?
メソッドでActiveRecordオブジェクトのvalid?
を呼び出せば良いかと思います。
errorsは統合する必要がある
2つ目のポイントで紹介したようにFormオブジェクト以外のモデルでvalidationを行った場合は気をつけることがもう1つあります。
通常validationに失敗するとモデルオブジェクトのerrors
にエラー情報が格納されるのですが、その情報はFormオブジェクトのerrors
には自動的には統合されません。
なので、Formオブジェクトのエラーとして扱うには各オブジェクトのerrors
をFormオブジェクトに統合する必要があります。
サンプルコードではvalid?
メソッドでその処理をしています。
まとめ
Formオブジェクトは単なるクラスなので、自分でいろいろと面倒を見る必要がありますが うまく使うとコードがすっきりするのでチャンスがあれば是非活用してみてください。
3日目は@awakiaさんです。よろしくお願いします。