Yuki's Tech Blog

仕事で得た知見や勉強した技術を書きます。

【Rails】【臨時更新】Railsのバリデーションでよく使うヘルパーをざっくりまとめてみた

目次

概要

Railsのバリデーションでよく使うヘルパーをざっくりまとめました。 まだ働いて1年も経っていないので、仕事を通して重要だなと感じたやつは随時追加していきます。

バリデーションでよく使うヘルパー

バリデーションって言葉はvalid(有効な)が由来なので、「有効か?、有効ではないか?」を検証しているんだと思っていただけると理解しやすいです。

presence

このヘルパーは、指定した属性が空でないことを検証します。属性が空であることが許されない場合に、使用します。

class Event < ApplicationRecord
  include Hashid::Rails

  belongs_to :user
 
  validates :reserve_date, presence: true
end

numericality

このヘルパーは、属性が数値のみを持つかどうかを検証します。整数値のみを許可するように指定するには、:only_integer を true に設定します。

class Event < ApplicationRecord
  include Hashid::Rails

  belongs_to :user
 
  # validates :price, numericality: trueと書くと、少数も通してしまう 
  validates :price, numericality: { only_integer: true }
end

only_integer以外にも、許容値に制約を加えるため様々なオプションが存在します。主に重さや値段など0より小さいことが許されない場合や、属性の値をある数値と比較して検証したい場合に、numericalityヘルパーを使用します。

class Event < ApplicationRecord
  include Hashid::Rails

  belongs_to :user
 
  validates :price, numericality: { greater_than_or_equal_to: 0 }

  # 0より大きい値のみ許容する場合は、greater_thanを使用する
  validates :price, numericality: { greater_than: 0 }

  # numericalityには複数のオプションを指定できる
  validates :contents_quantity, numericality: { only_integer: true, greater_than_or_equal_to: 1 }, allow_nil: true
end

length

このヘルパーは、属性の値の長さを検証します。何文字以上何文字以下を検証したい場合、inオプションを指定します。

class User < ApplicationRecord
  validates :name, length: { minimum: 2 }

  # inオプションは、属性の長さは、与えられた間隔に含まれなければならないことを検証します
  # 以下の場合、6文字以上20文字以下ならOK
  validates :password, length: { in: 6..20 }

  # isオプションは、属性の長さは、与えられた値と等しくなければならないことを検証します
  # 以下の場合、6文字であればOK
  validates :registration_number, length: { is: 6 }
end

(注) あくまでlengthオプションは、文字列の長さを検証するだけなので、数値の大小などは検証できません。数値の大小を検証したい場合、numericalityヘルパーを使用します。

comparison(Rails7)

Rails7からcomparisonヘルパーが追加されました。このヘルパーのおかげで、日付の検証で独自の検証メソッドを定義しなくても、検証できるようになりました。

このヘルパーは、比較可能な任意の2つの値の比較を検証します。主に日付の比較をしたい時に使用します。過去の日付で予約日を入力できないようにしたり、未来の日付で記録した日を入力できないようにしたい場合に使用します。

class Event < ApplicationRecord
  include Hashid::Rails

  belongs_to :user
 
  validates :date, comparison: { less_than_or_equal_to: proc { Date.today } }
  validates :reserve_date, comparison: { greater_than_or_equal_to: proc { Date.today } }, allow_nil: true
end

(注) procを書かないと挙動がおかしい時があるので、必ず書きましょう。

uniqueness

このヘルパーは、オブジェクトが保存される直前に属性の値が一意であることを検証します。DBの一意制約とは違うので、DB側にも一意制約を設定しておきましょう。メールなど、一意であってほしい値を検証するときにuniquenessヘルパーを使用します。

class User < ApplicationRecord
  validates :email, uniqueness: true
end

独自の検証メソッドを使ったバリデーション

validatesメソッドに用意されているヘルパーでは有効性を検証できない場合に使用します。事前に検証用のプライベートメソッドを定義しておいて、validateメソッドにそのプライベートメソッドを指定します。valid? メソッドは、errors コレクションが空であることを確認します。したがって、プライベートメソッド内で検証が失敗した場合、errors.addを実行する必要があります。モデルに定義すべきでないメソッドをモデル内に定義することは、ファットモデルの原因になるので、ヘルパーで対処できる場合は、そちらを優先的に利用しましょう。

class Event < ApplicationRecord
  include Hashid::Rails

  belongs_to :user

  validate :discount_cannot_be_greater_than_total_price
  # 省略

  private 

  def discount_cannot_be_greater_than_total_price
    if discount > total_price
      errors.add(:discount, "can't be greater than total value")
    end
  end
end

参考記事

Active Record Validations — Ruby on Rails Guides

【Rails】 Railsのバリデーションの使い方をマスターしよう! | Pikawaka

Rails 7 の便利な ComparisonValidator でちとハマった - Qiita