じぶんメモ

プログラミングのメモ、日常のメモとか。

railsのモデルに、特定の条件下で動くvalidationを追加

ifを使用した条件分岐

例えば、bという項目がtrueの場合のみaの値は必須にしたい、とかあると思う。
with_optionsメソッドを使用して以下のように実装することができる。

class Post < ActiveRecord::Base
  # ifオプションで条件に合致する場合のみvalidatesを実行(published?はメソッド名またはmodelのboolean項目)
  validates :name, presence: true if: published?

  # 複数valitatesをまとめたい場合はwith_optionsを使用
  with_options if: :published? do
    validates :name,  presence: true
    validates :category,  presence: true
  end

  # unlessで条件に合致しない場合のみvalidatesを実行
  # lambdaを使うことで複数条件を指定できる
  with_options unless: -> { :hoge? || :foo? } do
    validates :name,  presence: true
  end
end

ただし、この場合、with_optionsブロック内部で更にif: を使用してしまうと、
内部のif条件の場合にしかvalidatesが実行されなくなる。

  # hogeの時しかwith_options内部が実行されない
  with_options if: :published? do
    validates :name,  presence: true
    validates :category,  presence: true, if: hoge
  end

  # with_optionsで定義したものと逆のもの(この場合はunless)を使用すれば問題ない
  with_options if: :published? do
    validates :name,  presence: true
    validates :category,  presence: true, unless: 'hoge.blank?'
  end

ネストしたvalidationを記載するなら、メソッドを一つ作って指定した方が良さそう。

validates :name,  presence: true, if: :require_validation?

def require_validation?
  return true if aaa? && bbb?
  false
end

onを使用した条件分岐

例えば新規登録(create)の場合のみバリデーションをかけたい時は、onオプションを使用する。

class Post < ActiveRecord::Base
  # createの時のみ実行
  validates :name, presence: true on: :create

  # if同様with_optionsを使用できる
  with_options on: :create? do
    validates :name,  presence: true
    validates :category,  presence: true
  end

  # こうすることで、pattern_hogeコンテキストが渡された時のみvalidatesが実行される
  validates :hoge, presence: true, length: { maximum: 3000 }, on: :pattern_hoge
end
class PostsController < ApplicationController
  def hoge
    # pattern_hogeコンテキストを指定
    post.save(context: :hoge.save(context: :pattern_validation))
  end
end