3. バリデーション(5)
2012/09/24 シナジーマーケティング(株) 寺岡 佑起
Model 3章 バリデーション
3.1. バリデーションとは
3.2. バリデーションの定義
3.3. バリデーションの実行タイミング
3.4. バリデーションエラーの確認
3.5. ビューへの表示
3.6. 組み込みのバリデーション
3.7. 共通のバリデーションオプション
3.8. 独自ロジックでのバリデーション
3.9. カスタムバリデータの作成
3.8. 独自ロジックでのバリデーション
validates_each
validates_eachメソッドは、組み込みのバリデーションでは実現できない検証をモデル内で簡単に定義したい場合に利用します。
validates_eachには引数として任意の数の検証対象のフィールド名を与え、検証処理をブロック内で定義します。
ブロックはバリデーション時に実行され、引数に対象レコードのオブジェクト、フィールド名、値が与えられます。
ブロック内では検証を行い、エラーとしたい場合はerrorsのaddメソッドを呼び出してエラー情報を設定することができます。
validates_eachには、 共通のバリデーションオプション で紹介した共通のオプションのうち、allow_nil, :allow_blank, :on, :if, :unless を指定することができます。
# coding: utf-8 class Person < ActiveRecord::Base validates_each :first_name, :email do |record, attr, value| record.errors.add(attr, '佐藤さん以外登録することができません') unless value.include?("sato") end end
validate, validate_on_create, validate_on_update
複数のフィールドに対して検証が必要な場合、組み込みのバリデーションやvalidates_eachでは実現できないことがあります。
validateメソッドを利用すると、独自の検証処理を実装することができます。
validateメソッドは、引数にメソッド名のシンボルを与えるか、ブロック内に検証処理を実装することで定義します。
validate_on_create、validate_on_updateはそれぞれ新規登録、上書更新時のみ実行される以外はvalidateと同様です。
以下の2つの例はいずれも同じ検証を行います。
# coding: utf-8 class Person < ActiveRecord::Base validate :check_name def check_name errors.add(attr, '佐藤家では太郎さん以外登録することはできません') if first_name == "sato" and last_name != "taro" end end
# coding: utf-8 class Person < ActiveRecord::Base validate do errors.add(attr, :last_name, '佐藤家では太郎さん以外登録することはできません') if first_name == "sato" and last_name != "taro" end end
3.9. カスタムバリデータの作成
前節ではvalidates_eachやvalidateメソッドを使って、独自ロジックでバリデーションを行う方法を紹介しました。
これらの機能を利用する場合は、検証処理を各々のクラスに実装する必要がありました。
アプリケーション内で共通の検証を利用したい場合は、カスタムバリデータクラスを作成すると良いでしょう。
ActiveModel::Validatorクラスを継承
モデルクラスのvalidates_withメソッドにActiveModel::Validatorクラスを継承したクラスを指定することで、指定したクラスに実装したバリデーション処理を呼び出すことができます。
バリデータクラスを定義するには、ActiveModel::Validatorを継承して、validateメソッドに検証処理を記述します。
validateメソッドでは引数として与えられた対象レコードのオブジェクトに対して検証を行い、エラーの場合はerrorsのaddメソッドでエラー情報を設定します。
validates_withに与えたオプションは定義したバリデータクラスのインスタンス内では、optionsというメソッドで取得することができます。
検証処理を実装したクラスを用意することで、アプリケーション内で再利用することができるようになります。
特別な理由がない限り、次に紹介するActiveModel::EachValidatorを利用する方が便利です。
ActiveModel::EachValidatorクラスを継承
ActiveModel::EachValidatorクラスを継承したバリデータを作成することで、
1フィールドに対するバリデーションを行うクラスを作成することができます。
例として、電話番号のバリデータを作成してみましょう。
1. config/application.rb内にautoload_pathsの設定を追記します。
# Custom directories with classes and modules you want to be autoloadable. # config.autoload_paths += %W(#{config.root}/extras) config.autoload_paths += %W(#{config.root}/lib/validations)
2. lib/validations/phone_number_validator.rbにActiveModel::EachValidatorを継承したクラスを作成します。
検証処理はvalidate_eachメソッドに実装します。
validate_eachメソッドの引数には、対象レコードのオブジェクト、フィールド名、値が与えられます。
# coding: utf-8 # 電話番号の検証を行うバリデータ class PhoneNumberValidator < ActiveModel::EachValidator # 電話番号バリデーションを実行します def validate_each(record, attribute, value) record.errors.add(attribute, options[:message] || :phone_number) unless /\A[0-9]{10,11}\z/ === value.gsub('-', '') end end
3. validatesメソッドで、:phone_number => trueを指定することで、PhoneNumberValidatorの検証処理を利用できます。
class Person < ActiveRecord::Base validates :phone, :phone_number => true end