1つのカラムにvalidatesとvalidateをつけたときに気をつけたいこと
仕事中に引っかかりそうになったので、備忘録がてら書きたいと思います。
RailsにおけるValidatesとValidateの違い
Validates
validatesは特定のカラムに対して、予めRailsが用意している条件で
バリデーションをかけたい時に使うメソッドです。
例えば下記では、nameカラムに必須条件を加えています。
他にも一意条件である、uniquenessや値の長さをチェックするlengthなどがあります。
```
class Person < ApplicationRecord
validates :name, presence: true
end
```
Validate(カスタムメソッド)
カスタムメソッドとは、ユーザーが独自に作成出来る、モデルの状態を確認し不正な状態の時にerrorsコレクションにメッセージを追加することが出来る機能です。
例えば下記の様なMemberモデルがあるとします。
Memberモデルは入会日(start_date)と退会日(end_date)を持つとした時、
入会日は常に退会日より以前であるということをバリデーションの条件として
追加したい時は下記の様に設定できます。
```
class Member < ApplicationRecord
validate :check_start_date_before_end_date
def check_start_date_before_end_date
return if start_date < end_date
errors.add(:start_date, ": 入会日は退会日より後にはできません")
end
```
validatesメソッドもvalidateメソッドもRailsの開発では必須なので、使う機会は頻繁にあるので、ちゃんと理解しておかないといけなかったのですが、今回は下記の様なパターンでちょっと引っかかってしまいました。
同一カラムに対してvalidatesとvalidteを使う
ずばり下記の様な条件で引っかかりました。
```
class Member < ApplicationRecord
validates :start_date, presence: true
validates :end_date, presence: true
validate :check_start_date_before_end_date
def check_start_date_before_end_date
return if start_date < end_date
errors.add(:start_date, ": 入会日は退会日より後にはできません")
end
```
start_dateにもend_dateにも必須制約をつけていたので、すっかり油断していました。
これ、check_start_date_before_end_dateを通る時に、
start_dateもしくはend_dateがnilだった時に、
`NoMethodError: undefined method start_date for nil:NilClass`
で落ちてしまうんですね。
バリデーションはそれぞれ独立している
バリデーションでは、どこかで制約に引っかかった、引っかかっていない関係なしに、
設定しているバリデーションを全て実行する訳ですね。
そして、制約に引っかかったものは全てerrorsに追加され、
最終的にerrors.empty?がtrueならvalidはtrue
1つ以上でもエラーメッセージが追加されており、errors.empty?がfalseなら
validはfalseになるわけです。
ついつい、validatesで必須制約は作ったから
validateではチェックしなくていいと安心しがちですが、気をつけるべきでした。