Part5 続き
前回の記事では、カスタムバリデーションについてまとめました📙
今回はバリデーションエラーを管理するerrorsについてまとめていきたいと思います!✍️
バリデーションエラーに対応
データの保存時やvalid?メソッドでバリデーションが実行されますが、失敗したらそれで終わりではなく、どの属性がどのような理由で失敗したのかを知る必要があります。Railsでは、バリデーションエラーにアクセスできるようにerrorsメソッドが用意されています。
errorsメソッドは、ActiveModel::Errorsオブジェクトを返し、バリデーションエラーに関する情報を提供します。以下では、errorsオブジェクトの主なメソッドとその使い方について説明します。
基本的な使い方
よく使用されるerrorsオブジェクトのメソッドをいくつか紹介します!
先に今回出てくるものをまとめておきます✍️
| メソッド名 | 説明 |
|---|---|
full_messages |
すべてのエラーメッセージを配列で返す |
errors[:attribute] |
指定した属性に関連するエラーメッセージの配列を返す |
errors.where |
条件に一致するエラーメッセージの配列を返す |
any? |
エラーが存在するかどうかを真偽値で返す |
empty? |
エラーが存在しないかどうかを真偽値で返す |
size / count |
エラーの数を返す |
add |
エラーメッセージを手動で追加する |
errors[:base] |
オブジェクト全体の状態に関連するエラーを追加できる |
clear |
すべてのエラーメッセージをクリアする |
full_messages
full_messagesメソッドは、オブジェクトのすべてのエラーメッセージを配列で返します!
各メッセージは属性名とエラーメッセージを組み合わせた形式で提供されます。
エラーメッセージをユーザーに表示する際によく使われます。
class User < ApplicationRecord
validates :name, presence: true
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
end
user = User.new(name: "", email: "invalid_email")
user.valid? # => false
user.errors.full_messages
# => ["Name can't be blank", "Email is invalid"]
errors[:attribute]
特定の属性に関連するエラーメッセージを取得したい場合は、errors[:attribute]の形式でアクセスします!
この方法では、指定した属性に関連するエラーメッセージの配列が返されます。
属性に関連するエラーがない場合は、空の配列が返されます。
class Company < ApplicationRecord
validates :name, presence: true, uniqueness: true
end
company = Company.new(name: "")
company.valid? # => false
company.errors[:name]
# => ["Name can't be blank"]
errors.where
各エラーはActiveModel::Errorオブジェクトとして表現されており、errors.whereメソッドを使用して条件に一致するエラーメッセージの配列を取得できます。
class Product < ApplicationRecord
validates :name, presence: true, length: { minimum: 3 }
end
product = Product.new
product.valid? # => false
product.errors.where(:name)
# => [#<ActiveModel::Error attribute=:name, type=:blank>, #<ActiveModel::Error attribute=:name, type=:too_short, options={:count=>3}>]
このようにerrors.where(:attr)の第1引数に属性名を指定すると、その属性に関連するエラーオブジェクトの配列が返されます。
第2引数にtypeオプションを指定することで、特定の種類のエラーをフィルタリングすることも可能です。
product.errors.where(:name, type: :blank)
# => [#<ActiveModel::Error attribute=name, type=blank, options={}>]
これらによって取得したerrorオブジェクトから情報を取り出すことができます。
error = product.errors.where(:name).last
error.message
# => "is too short (minimum is 3 characters)"
error.full_message
# => "Name is too short (minimum is 3 characters)"
error.attribute
# => :name
error.type
# => :too_short
error.options[:count]
# => 3
any? / empty?
any?メソッドは、オブジェクトにエラーが存在するかどうかを真偽値で返します。一方、empty?メソッドは、エラーが存在しないかどうかを真偽値で返します!
class Order < ApplicationRecord
validates :total_amount, numericality: { greater_than: 0 }
end
order = Order.new(total_amount: -10)
order.valid? # => false
order.errors.any?
# => true
order.errors.empty?
# => false
any?のよくある使い方として、Viewでエラーがあればfull_messagesを表示するときの判定で使用されています。
size / count
sizeメソッドまたはcountメソッドを使用して、オブジェクトに存在するエラーの数を取得できます!
class Customer < ApplicationRecord
validates :name, presence: true
validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }
end
customer = Customer.new(name: "", email: "invalid_email")
customer.valid? # => false
customer.errors.size
# => 2
customer.errors.count
# => 2
エラーメッセージにて「〇〇件のエラーがあります」といった表現をする際に、sizeやcountメソッドが役立ちます。
add
addメソッドを使用して、手動でエラーメッセージを追加することができます!
具体的には「属性名」「エラー種別」「オプションの追加ハッシュ」を引数として受け取り、errorオブジェクトを作成します。
このメソッドは、カスタムバリデーションメソッド内で細かくエラー内容を設定したいときによく使用されます。
class School < ApplicationRecord
validate do |school|
if school.students.count < 10
school.errors.add(:students, :too_few, message: "生徒数が少なすぎます。最低10人必要です。")
end
end
end
errors[:base]
errors[:base]を使用すると、オブジェクト全体の状態に関連するエラーを追加できます!
「どの属性の問題か特定できない、またはモデル全体に関わる問題」に対してエラーメッセージを設定する際に使われます。
class Booking < ApplicationRecord
validate :validate_booking_rules
private
def validate_booking_rules
# 特定の曜日は予約不可
if booking_date.wday == 0 # 日曜日
errors.add(:base, "日曜日は予約できません")
end
# 同じ時間帯に既に予約がある
if conflicting_bookings_exist?
errors.add(:base, "選択した時間は既に予約されています")
end
# 予約期限を過ぎている
if booking_date < 3.days.from_now
errors.add(:base, "予約は3日前までに行ってください")
end
end
end
booking = Booking.new(booking_date: Date.today)
booking.valid? # => false
booking.errors[:base]
# => ["日曜日は予約できません", "選択した時間は既に予約されています", "予約は3日前までに行ってください"]
:baseを使うべき場面
- 複数の属性にまたがる検証
- モデル全体のビジネスルール
- 外部API連携のエラー
- どの属性にも紐づけられないエラー
メリット
- ユーザーに全体的な問題を伝えられる
- 特定のフィールドに紐づけない柔軟性
- 複雑なビジネスロジックに対応
clear
clearメソッドを使用して、オブジェクトのすべてのエラーメッセージをクリア(削除)することができます!
ただ、valid?メソッドや保存メソッドを呼び出すと自動的にクリアされるため、手動でクリアする必要はあまりありません。
class User < ApplicationRecord
validates :name, presence: true
end
user = User.new(name: "")
user.valid? # => false
user.errors.clear
user.errors.any?
# => false
おわりに
長かったRailsバリデーションシリーズも今回で終了です!🎉
バリデーションについて全体像が掴めたと思いますので、実際のプロダクトでも意図していないデータが保存されないように積極的に活用したいと思います!
最後まで見ていただき、ありがとうございました😊
参考資料
これまでのバリデーションに関する記事
