はじめに
カスタムバリデータを実装する機会があったので、学習内容をまとめます。
カスタムバリデータ
メリット
- 組み込みのバリデータだけでは足りない場合に独自のバリデータを追加できる
- 特定のモデルから分離して定義し、複数のモデルで共通利用できる=繰り返しの記述を防ぐことができる
組み込みバリデータ
バリデータ | バリデーション内容 |
---|---|
absence | 存在してはならないこと |
acceptance | フォームが送信されたときにユーザーインターフェイス上のチェックボックスがオンになっているか |
confirmation | 2つのテキストフィールドの入力内容が完全に一致するか |
comparison | 比較可能な2つの値を比較 |
format | withオプションで与えられた正規表現と属性の値がマッチするか |
inclusion | 値がセット内に存在すること |
exclusion | 値がセット内に存在しないこと |
length | 属性の値の長さを検証 |
numericality | 属性に数値のみが使われていること |
presence | 指定された属性が空(empty)でないこと |
uniqueness | オブジェクトが保存される直前に、属性の値が一意(unique)であり重複していないこと |
validates_associated | 常に有効でなければならない関連付けがモデルにある場合、オブジェクトを保存しようとするたびに、関連付けられているオブジェクトごとにvalid?を呼び出す |
validates_each | 属性をブロックでバリデーションする。バリデーション関数を独自に作成する必要がある |
validates_with | バリデーション専用の別クラスにレコードを渡す。バリデーション専用の別クラスは独自に作成する必要がある |
定義方法(ActiveModel::Validator)
-
app/models配下に直接ファイルを作成するか、validatorディレクトリを作成してそこにまとめてファイルを配置する
-
validate
メソッドを定義する -
引数に
record
(バリデーション対象のオブジェクト)を一つ取る
class HogeValidator < ActiveModel::Validator
def validate(record) # レコードを一つ引数に取る
unless record.name.start_with? "X" # record に対するバリデーションルール
record.errors.add :name, "名前はXで始まる必要があります"
end
end
end
-
validates_with
メソッドで呼び出す
class Hoge < ApplicationRecord
validates_with HogeValidator
end
定義方法(ActiveModel::EachValidator)
-
validate_each
メソッドをていg -
record
、attribute
、value
の三つの引数を取る-
record
: バリデーション対象のオブジェクト -
attribute
: バリデーション対象の属性名 -
value
: バリデーション対象の属性値
-
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless URI::MailTo::EMAIL_REGEXP.match?(value) # valueに対するバリデーションルール
record.errors.add attribute, (options[:message] || "はメールアドレスではありません")
end
end
end
- 標準のバリデーションと組み合わせて呼び出す
- カスタムバリデータのクラス名を
XXXValidator
とすると、xxx: true
と指定する(例:EmailValidator
をカスタムバリデータのクラス名とした場合、email: true
と指定する)
- カスタムバリデータのクラス名を
class Person < ApplicationRecord
validates :email, email: true
end
その他のカスタム
カスタムメソッド
- 定義方法
-
validate :カスタムメソッド名
の形でシンボルで渡す - 複数のシンボルを渡せる
- バリデーションは登録されたとおりの順序で実行される
-
class Invoice < ApplicationRecord
validate :expiration_date_cannot_be_in_the_past,
:discount_cannot_be_greater_than_total_value
def expiration_date_cannot_be_in_the_past
if expiration_date.present? && expiration_date < Date.today
errors.add(:expiration_date, "過去の日付は使えません")
end
end
def discount_cannot_be_greater_than_total_value
if discount > total_value
errors.add(:discount, "合計額を上回ることはできません")
end
end
end
-
:on
オプションを使えば、カスタムバリデーションが実行されるタイミングを変更できる - 使えるのは
on: :create
on: :update
class Invoice < ApplicationRecord
validate :active_customer, on: :create
おわりに
validatesとvalidateの使い分けがややこしいです。カスタムバリデータを定義する時はvalidate、呼び出す時はvalidatesです。ただし、カスタムメソッドはvalidateで登録します。
参考