はじめに
こんにちは株式会社のナオキです!
今回はRailsのバリデーションに関する記事になります。
バリデーションはアプリケーションの品質を保つ為の重要な要素です。
基本的な文字数制限等のバリデーションであればRailsの標準機能で対応できますが、それでは対応できないケースに出会うことがあります。
そのような場合にカスタムメソッドを活用し、要件を満たす方法を紹介します。
この記事では以下の内容について取り上げています。
- Railsの標準バリデーションの紹介
- カスタムメソッドの必要性
- ユースケースごとの実装例
- Rails初学者の方
Railsの標準バリデーションの紹介
属性の値が空でないことを確認します。
validates :title, presence: true
属性の値が一意であることを確認します。
validates :email, uniqueness: true
属性の値が指定した正規表現と一致するか確認します。
今回with
で指定している正規表現はメールアドレス用フォーマットの正規表現になります。
validates :email, format: { with: /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i }
属性の値が数値のみか確認します。greater_than
はnumericality
のオプションで指定した数値より大きいかを確認しています。
validates :price, numericality: { greater_than: 0 }
属性の値が文字列で指定された文字数かどうかを確認します。minimum
, maximum
はlength
のオプションで最低値と最高値を指定しています。
validates :password, length: { minimum: 6, maximum: 20 }
他にもバリデーションとオプションが多数あるので気になる方は以下を参照してください!
カスタムメソッドの必要性
Railsには上記のような便利なバリデーションが標準機能としてあります。
基本的なバリデーションは標準機能で事が済みますが、複雑な要件を満たすためには、独自のバリデーションが不可欠になってきます。
それらをユースケースとともに紹介していきます!
ユースケースごとの実装例
標準のバリデーションでは、2つ以上の属性の関係性を考慮することができません。
なので、カスタムメソッドで実装していきます。
例:開始日と終了日を設定する場合に、「終了日が開始日よりも後でなければいけない」というバリデーションを追加したい。
class Reservation < ApplicationRecord
validate :end_date_after_start_date
def end_date_after_start_date
if end_date <= start_date
errors.add(:end_date, "終了日は開始日より後の日付を選択してください")
end
end
end
if end_date <= start_date
で、終了日が開始日より後かの判定をしています。errors.add
を使用して、終了日が開始日と同日かそれ以前の場合のエラーメッセージを設定しています。
同日かそれ以前だった場合に、「終了日は開始日より後の日付を選択してください」とエラーメッセージが表示されます。
標準のバリデーションでは、他のモデルのデータとの比較することができません。
例:注文する場合に、「注文数が在庫数を超えていたら注文できない」というバリデーションを追加したい。
class Order < ApplicationRecord
validate :stock_must_sufficient
def stock_must_sufficient
if quantity > product.stock
errors.add(:quantity, "注文数が在庫数を超えています。")
end
end
end
if quantity > product.stock
で、注文書が在庫数を超えてるかの判定をしています。
超えていた場合に、「注文数が在庫数を超えています」とエラーメッセージが表示されます。
標準のバリデーションでは、他のレコードの情報を参照できません。
例:レンタルスペースで、「各部屋は1つの時間帯に1つの予約しかできない」というバリデーションを追加したい。
class Reservation < ApplicationRecord
validate :no_time_overlap
def no_time_overlap
if Reservation.where(room_id: room_id)
.where.not(id: id)
.where("start_time < ? AND end_time > ?", end_time, start_time)
.exists?
errors.add(:base, "この時間帯はすでに予約されています")
end
end
end
.where(room_id: room_id)
で部屋の情報を取得し、.where.not(id: id)
で自分自身のid
を除外しています。予約を更新する場合、自分自身のid
を考慮していないとエラーが発生するからです。.where("start_time < ? AND end_time > ?", end_time, start_time)
で他の予約との時間が被っていないかを確認しています。
最後に.exists?
で条件に合うレコードが存在したらtrue
を返し、「この時間帯はすでに予約されています」とエラーメッセージが表示されます。
おわりに
今回はRailsのカスタムメソッドでのバリデーションについての紹介をしました。
大体のバリデーションは標準機能で実装できますが、複雑な要件などでは実装できない場合もあります。
そのような場合は、独自のカスタムメソッドを作成し、バリデーションを柔軟に実装していきましょう!