1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rails初学者】バリデーションの基礎についてざっくり理解する -Part5(カスタムバリデーション)

Last updated at Posted at 2025-12-14

Part4 続き

前回の記事では、バリデーションで使用できる共通オプションについてまとめました📙
今回はカスタムバリデーションについてまとめていきたいと思います!✍️

validation-flow3.png

カスタムバリデーション

Railsには様々な組み込みバリデーションが用意されていますが、それだけでは対応できない場合もあります。そんなときに役立つのがカスタムバリデーションです!
カスタムバリデーションを使用すると、独自のバリデーションロジックを定義して、モデルの属性に対して特定のルールを適用することができます。以下では、カスタムバリデーションの作成方法について説明します。

カスタムバリデータ

ActiveModel::Validatorを使うケース

カスタムバリデータを作成するには、ActiveModel::Validatorを継承したクラスを定義し、その中でvalidateメソッドを実装します!
validateメソッド内で、バリデーションロジックを記述し、条件に合わない場合はrecord.errors.addメソッドを使用してエラーメッセージを追加します。
カスタムバリデータをモデルに適用するには、validates_withメソッドを使用します。

class CategoryValidator < ActiveModel::Validator
  def validate(record)
    unless ['Ruby', 'JavaScript', 'TypeScript'].include?(record.category)
      record.errors.add(:category, "is not a valid category")
    end
  end
end

class Article < ApplicationRecord
  validates_with CategoryValidator
end

上記の例では、CategoryValidatorクラスがカスタムバリデータとして定義されており、Articleモデルに適用されています。
category属性が指定されたリストに含まれていない場合、エラーメッセージが追加されます。

ActiveModel::EachValidatorを使うケース

個別の属性に対してカスタムバリデータを追加するには、ActiveModel::EachValidatorを継承したクラスを定義し、その中でvalidate_eachメソッドを実装すると、モデル内でvalidatesメソッドを使用して適用できます!

class PhoneNumberValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    if value.present? && !value.match?(/\A\d{10,11}\z/)
      record.errors.add(attribute, options[:message] || "is not a valid phone number")
    end
  end
end

class User < ApplicationRecord
  validates :phone_number, phone_number: true
end

上記の例では、PhoneNumberValidatorクラスがカスタムバリデータとして定義されており、Userモデルのphone_number属性に適用されています。
phone_number属性が10桁または11桁の数字でない場合、エラーメッセージが追加されます。
モデルでは、validatesメソッドを使用して、属性名: trueの形式でカスタムバリデータを指定します。

validates_eachメソッドは3つの引数を受け取るように実装する必要があり、引数は下記の通りです。

  • record: バリデーション対象のオブジェクト
  • attribute: バリデーション対象の属性
  • value: バリデーション対象の属性の値

ValidatorEachValidatorの使い分け
判断基準としては以下の通りです!

Q.そのルールは、複数のカラムを比較する必要がありますか?

  • Yes→Vaidatorを使用する(例:パスワードとパスワード確認、開始日と終了日)
  • No→次の質問へ

Q.そのルールは、個別のカラムだけ見れば判断できますか?

  • Yes:EachValidatorを使用する(例:電話番号、郵便番号、メールアドレス)

EachValidatorのメリットは、モデル側のコードの可読性が向上することです!
個別の属性に対してカスタムのバリデータを作成する際には、EachValidatorを使用することを検討してみてください😌

カスタムメソッド

カスタムメソッドを使用してバリデーションを定義することもできます!
モデル内でvalidateメソッドを使用して、独自のバリデーションメソッドをシンボルで指定します。

class User < ApplicationRecord
  validate :phone_number_format

  private
  def phone_number_format
    if phone_number.present? && !phone_number.match?(/\A\d{10,11}\z/)
      errors.add(:phone_number, "is not a valid phone number")
    end
  end
end

Q. カスタムメソッドか、カスタムバリデータ(EachValidator)のどちらを使用すべきか?

カスタムバリデータ(EachValidator)が推奨です!
理由としては、

  • 再利用ができる
  • コードの可読性が向上する

再利用ができる」ですが、例えば「電話番号」という概念は、Userモデル以外にも登場する可能性が非常に高いです。 例えば、将来的にCompany(会社)やStore(店舗)モデルを作った時、下のコードなら一度定義したものを使い回すだけで済みます。

class Company < ApplicationRecord
  validates :phone_number, phone_number: true
end

class Store < ApplicationRecord
  validates :phone_number, phone_number: true
end

カスタムコンテキスト

バリデーションの実行タイミングをカスタムコンテキストで制御することも可能です!
onオプションでカスタムの名前を指定できます。

カスタムコンテキストの利用場面としては、ウィザード形式の複数ステップがあるフォームがあります。
ウィザード形式のフォームとは、Amazonや楽天の「購入手続き」ページでよく見かける、複数のステップに分かれたフォームのことです。

class User < ApplicationRecord
  validate :personal_information, on: :personal_info
  validate :contact_information, on: :contact_info
  validate :location_information, on: :location_info

  private
    def personal_information
      errors.add(:base, "名前は省略できません") if first_name.blank?
      errors.add(:base, "年齢は18歳以上でなければなりません") if age && age < 18
    end

    def contact_information
      errors.add(:base, "メールアドレスは省略できません") if email.blank?
      errors.add(:base, "電話番号は省略できません") if phone.blank?
    end

    def location_information
      errors.add(:base, "住所は省略できません") if address.blank?
      errors.add(:base, "市区町村名は省略できません") if city.blank?
    end
end

参考:Railsガイド カスタムコンテキストより

Part6へ続く

ようやくカスタムバリデーションまでたどり着きました...!🎉
次回は、errorsオブジェクトの使い方についてまとめていきたいと思います!✍️

参考記事

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?