###About
カスタムバリデーションで他のモデルのカラムを使いたい場合の記載方法について記載しています。
###Environment
この記事ではmacbook(unix)にインストールしたruby 2.5.1p57, Rails 5.2.3を使用しています。
#Validationとは
「バリデーション」とは、「検証、実証、認可、妥当性」を意味する英単語
「質的な良し悪し」を判断するのではなく、「システム的な適合不適合」を判断するための言葉
小難しく書きましたが、意図しないデータをDBに登録できない様、バリデーションをかけることが一般的であるそうです。
##カスタムバリデーション
通常、バリデーションは以下の様な形式で設定しますが、複雑なバリデーションを設定したい場合、自作のバリデーション(= カスタムバリデーション)を作成します。
class User < ApplicationRecord
validates :user_id, presence: true
validates :email, presence: true
end
基本的には、以下の様に定義したメソッド名をvalidate に続けて記載することで、設定可能です。
class User < ApplicationRecord
validates :user_id, presence: true
validates :email, presence: true
validate :if_user_does_not_have_nickname
def if_user_does_not_have_nickname
return if nickname.present?
errors.add(:nickname, "Nickname is absent")
end
end
##他のモデルのカラムも組み合わせたい場合
validationを考えていると、他のモデルのカラムを条件として加えたい時があるかと思います。
その様な場合、モデル間でアソシエーションを組むことで他のモデルのカラムを使用することができます。
(例)睡眠時間を記録するアプリ
・日付がデータとして渡されている
・もしユーザの睡眠記録が一つもなければ、睡眠記録の新規作成が可能
・睡眠記録がすでにある場合は、渡されたデータが既存の睡眠記録の最終日付の翌日にマッチしているかどうか調べる
・ミスマッチの場合、渡されたデータを新規登録しない
class Sleep < ApplicationRecord
# アソシエーションの設定。読み込み順の関係で、カスタムバリデーションより必ず上に記載してください。
belongs_to :user
validates :slept_time, presence: true
validates :wakeup_time, presence: true
# カスタムバリデーションの呼び出し(コントローラでnew, createメソッドの場合のみ)
validate :dates_cannnot_be_registered_if_there_is_no_yesterdays_date, on: [:new, :create]
# カスタムバリデーションの作成
def dates_cannnot_be_registered_if_there_is_no_yesterdays_date
# もし日付があり、ユーザの睡眠記録がない場合は処理を抜ける(= そのまま新規登録する。)
return if date.present? && user.sleeps.blank?
# もし日付がユーザの最新の睡眠記録の日付の翌日でない場合は、データを新規登録しない。
if date != user.sleeps.last.date.tomorrow
errors.add(:date, "You can't register that there is no data about before days.")
end
end
end
#最後に
いかがでしょうか。アソシエーションを組めていれば、意外と簡単に実装できてしまいます。
私はbelongs toの記述位置がカスタムバリデーションの下になっていたため、エラーと何時間も格闘する羽目になりましたが...。
参考になれば幸いです!
###筆者について
TECH::EXPERTにて4月よりruby, railsを学習している未経験エンジニアです。
記載内容に不備・不足があればご指摘いただけると幸いです。
至らぬ点ばかりですので、改善点がありましたらどんどんご指摘下さい!