今回取り上げるのは、Active RecordとActive Modelを使ったバリデーション機能の実装です。
Webアプリケーションでユーザが入力されたデータが正しい形式であるかを確かめるには、バリデーションチェックを行います。
またデータに誤りがあれば、それをユーザに伝えて再度値を入力し直してもらう必要があります。

データベースの制約を用いる方法もありますが、Railsのモデルを用いることでデータに関するエラーをアプリケーション層でまとめて処理できるようになります。

オンラインでプログラミング学習を始めてみたいなら、米国シリコンバレー発祥の世界最大級のオンライン学習プラットフォーム、Udemyがおすすめです。
こちらの記事で紹介するフレームワーク、Ruby on Railsも学ぶことができます!
よくわかるRuby on Rails入門 オンライン講座

バリデーションとは

バリデーションは、アプリケーション中でデータベースにレコードを登録する際や更新する際に、そのデータが正しい形式であるのかどうかを検証する機能です。

アプリケーション中でデータベースのレコードを扱う際には、データの形式に制限を設けたい場合がほとんどです。
そういった場合、バリデーションを設定しておくことで値を自動的に検証し、値が適切でないときにはエラーメッセージをユーザに対して表示することができます。

今回はイベントを管理するサンプルアプリケーション上にバリデーションを実装していきます。
同じ環境で試してみたい方は、以下に従ってコマンドを実行しておいてください。

# 新規プロジェクトの作成
$ rails new event-app
$ cd event-app

# scaffoldコマンドで各種モデルの関連ファイルを自動生成
$ rails generate scaffold event name:string hold_date:date
$ rails generate scaffold user name:string email:string password:string

# データベースの作成
$ rails db:create

# マイグレーションの実行
$ rails db:migrate

現場を意識した本格的な学習カリキュラムで、本物のスキルを身につけたいならこちら!
困ったときにいつでも相談できるバディ制度でわからないこともすぐに解決できます。
プログラミングスクールといえば【RUNTEQ】

バリデーションの設定方法

基本的なバリデーションの設定は以下のようにActiveRecordオブジェクト内に記述します。
必要な情報は、バリデーションをかけたいカラム名と制約の内容です。

class Event < ApplicationRecord
  validates :name, presence: true
end

これは、Eventモデルのnameというカラムがnil(空)でないようにしたいときのバリデーションです。
presenceは「存在すること」という意味です。
では、この時にnameを空にしてデータを登録しようとした場合、どうなるか試してみましょう。

$ rails console

irb(main):001:0> event = Event.new
   (0.9ms)  SELECT sqlite_version(*)
=> #<Event:0x000002b12fb3c170 id: nil, name: nil, hold_date: nil, created_at: nil, updated_at: nil>

irb(main):002:0> event.name = ''
=> ""

irb(main):003:0> event.hold_date = Time.parse('20220101')
=> 2022-01-01 00:00:00 +0900

irb(main):004:0> event.save
=> false

falseが返されましたので、バリデーションが機能しているようです。

どのようなバリデーションに引っ掛かってしまったのかを確認してみます。

irb(main):005:0> event.errors.full_messages
=> ["Name can't be blank"]

Nameカラムが空であることを指摘されていますね。
正しくバリデーションがかかっていることが確認できました。

全てのバリデーションチェックを通過した場合、trueが返され保存に成功します。
バリデーションのチェックのみを行い、データの保存などはしてほしくないときには、vaild?メソッドを使います。

irb(main):006:0> event.name = 'sample event'
=> "sample event"

irb(main):007:0> event.valid?
=> true

irb(main):008:0> event.save
  TRANSACTION (0.1ms)  begin transaction
  Event Create (1.5ms)  INSERT INTO "events" ("name", "hold_date", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "sample event"], ["hold_date", "2022-01-01"], ["created_at", "2021-11-07 06:23:04.564754"], ["updated_at", "2021-11-07 06:23:04.564754"]]
  TRANSACTION (3.4ms)  commit transaction
=> true

また、データベースの制約も同時に行った方がシステムが安定しますので、一般的には2重チェックを行ったりします。

エクスクラメーション(!)付きメソッド

ActiveRecordにはエクスクラメーション(!)の付いたメソッドが存在します。
保存操作を行う、createメソッド、saveメソッド、updateメソッドなどです。
エクスクラメーションが付いたものとついていないものが存在し、それぞれ動作が違います。

エクスクラメーションの付いていないメソッドは、バリデーション失敗時にfalseを返すだけです。
一方エクスクラメーションの付いたメソッドは、バリデーション失敗時に例外を返します

$ rails console

irb(main):001:0> event = Event.new
   (0.8ms)  SELECT sqlite_version(*)
=> #<Event:0x000002b5c0afe628 id: nil, name: nil, hold_date: nil, created_at: nil, updated_at: nil>

irb(main):002:0> event.name = ''
=> ""

irb(main):003:0> event.hold_date = Time.parse('20230101')
=> 2023-01-01 00:00:00 +0900

irb(main):004:0> event.save
=> false

irb(main):006:0> event.save!
C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/activerecord-6.1.4.1/lib/active_record/validations.rb:80:in `raise_validation_error': Validation failed: Name can't be blank (ActiveRecord::RecordInvalid)

saveメソッドでバリデーションエラーが発生した場合はfalseを返しています。
しかし、save!メソッドでバリデーションエラーが発生した場合は例外を発生させていることがわかります。

これらは用途に応じて使い分けましょう。
エクスクラメーション付きのメソッドは開発時の動作確認などにも使えますので、ぜひ覚えておきましょう。

各種バリデーション

様々な種類のバリデーションについて見ていきます。
また、ひとつのデータに対してバリデーションは複数指定することができます。

空白バリデーション

presenceルールでは、データがnilでないことを検証します。
必ずデータが存在してほしい場合に使います。
absenceルールでは、データがnilであることを検証します。
データが存在してほしくない場合に使います。

また、allow_blankは明示的にnilを許可する共通オプションです。
別のバリデーションによってnilが禁止されたときにそれを解除するために使います。

class Event < ApplicationRecord
  validates :name, presence: true
  # validates :name, absence: true
  # validates :name, allow_blank: true
end

チェックボックスのバリデーション

acceptanceルールでは、チェックボックスがオンになっていることを検証します。
利用規約に同意を求めたい場合などに用います。

class Event < ApplicationRecord
  validates :name, acceptance: true
end

確認項目のバリデーション

confirmationルールでは、2つのテキストフィールドの値が一致するかを検証します。
メールアドレスやパスワードの確認用フォームに使用します。

一致するかの検証は確認用フィールドがnilではない場合にのみおこなわれるため、空白チェックと併用することが多いです。

case_sensitiveオプションにより大文字小文字を区別するかを指定することもできます(デフォルトはtrue)。

class User < ApplicationRecord
  validates :password, confirmation: true
  validates :password_confirmation, presence: true
  validates :email, confirmation: { case_sensitive: false }
  validates :email_confirmation, presence: true
end

また、ビュー側には通常のフォームフィールドと_confirmationを付け加えたフィールドを用意します。

<%= text_field :person, :email %>
<%= text_field :person, :email_confirmation %>

文字数のバリデーション

lengthルールでは、データの文字数による検証を行います。
minimumあるいはmaximumでは、文字数の下限や上限を指定できます。
inでは、文字数の範囲を指定できます(minimumとmaximum両方を設定した動作と同じ)。
isでは、指定した文字数とぴったり同じであるかを検証します。

class Event < ApplicationRecord
  validates :name, length: { minimum: 5 }
  # validates :name, length: { maximum: 20 }
  # validates :name, length: { minimum: 5, maximum: 20 }
  # validates :name, length: { in: 5..20 }
  # validates :name, length: { is: 10 }
end

重複のバリデーション

uniquenessルールでは、一意性の検証を行います。
データベースに既に重複するデータが存在する場合はfalseを返します。

case_sensitiveオプションにより大文字小文字を区別するかを指定することもできます(デフォルトはtrue)。

class Event < ApplicationRecord
  validates :name, uniqueness: true
  validates :name, uniqueness: { case_sensitive: false }
end

数値のバリデーション

numericalityルールでは、値が数値であるかを検証します。
デフォルトではnilが許容されていないため、入力が任意のカラムである場合には明示的にallow_blankをtrueにする必要があります。

class Event < ApplicationRecord
  validates :capacity, numericality: true
end

numericalityルールでは以下のようなオプションが使えます。

only_integerオプションは数値の中でも整数値であるかを検証します。
greater_thangreater_than_or_equal_toオプションはそれぞれ指定値より大きい、指定値以上であるかを検証します。
equal_toオプションは指定値と一致するかを検証します。
less_thanless_than_or_equal_toオプションはそれぞれ指定値未満、指定値以下であるかを検証します。
oddevenオプションはそれぞれ奇数、偶数であるかを検証します。

class Event < ApplicationRecord
  validates :capacity, numericality: { only_integer: true }
  # validates :capacity, numericality: { greater_than: 100 }
  # validates :capacity, numericality: { greater_than_or_equal_to: 100 }
  # validates :capacity, numericality: { equal_to: 1000 }
  # validates :capacity, numericality: { less_than_or_equal_to: 20000 }
  # validates :capacity, numericality: { less_than: 20000 }
  # validates :capacity, numericality: { odd: true }
  # validates :capacity, numericality: { even: true }
end

フォーマットバリデーション

formatルールでは、正規表現にマッチするかを検証します。

class Event < ApplicationRecord
  validates :name, format: { with: /\A[a-zA-Z]+\z/ }
end

値を含む・含まないのバリデーション

inclusionルールでは、入力値が指定した値の中に含まれているかを検証します。
exclusionルールでは、入力値が指定した値の中に含まれていないかを検証します。

セレクトボックスなどで不正な値が送信されていないかを検証するなどの目的で使用します。

class Event < ApplicationRecord
  validates :status, inclusion: { in: %w(archive, in_session, scheduled) }
  validates :phase, exclusion: { in: %w(cancel, rehearsal) }
end

プログラミングでわからないことがあったら、その道のプロに質問するのが一番!
現役で活躍中のエンジニアと繋がって、効率的にWeb開発のスキルを身につけましょう。
プログラミング学習のスキルプラットフォーム【MENTA】

エラーメッセージについて

バリデーションでエラーが発生した場合、画面上にその内容を表示してユーザに伝えたいという場合がほとんどです。
Railsではバリデーションが行われた後にエラーがあった場合は、それらをmodel.errorsに格納します。

注意する点として、バリデーションを行っていない状態ではエラーは存在しません。
明示的にエラーを取得したい場合には、valid?メソッドなどによりバリデーションを実行後にerrorsを参照します。

エラーメッセージの変更

デフォルトのエラーメッセージを変更したい場合は、message共通オプションを用います。

class Event < ApplicationRecord
  validates :name, presence: { message: "イベント名を入力してください。" }
end

messageオプション内では以下の変数が利用可能で、メッセージを表示する際にそれぞれ動的に変化します

valueはユーザがフィールドに入力した値に置き換えられます。
modelattributeはそれぞれモデル名、カラム名に置き換えられます。
countはmaximumやminimumなどの特定のオプションで指定された数値に置き換えられます。

class Event < ApplicationRecord
  validates :name, nuiqueness: { message: "%{value}はすでに使われています。" }
end

エラーメッセージの表示

バリデーションエラーのコレクションはerrors.messagesで取得できます。
また、エラーの全文はerrors.full_messagesで取得できます。

$ rails console

irb(main):001:0> event = Event.new
   (1.5ms)  SELECT sqlite_version(*)
=> #<Event:0x0000021f337d8fd0 id: nil, name: nil, hold_date: nil, created_at: nil, updated_at: nil>

irb(main):002:0> event.valid?
=> false

irb(main):003:0> event.errors.messages
=> {:name=>["can't be blank"]}

irb(main):004:0> event.errors.full_messages
=> ["Name can't be blank"]

以下は実際にアプリケーションでよく使う例です。
ビュー側でユーザに対してエラーメッセージを表示したいときに使います。

errors.any?の部分でエラーがあるかをチェックし、エラーがあった場合にはerrors.full_messagesで全エラー文を取得しています。

<% if model.errors.any? %>
  <ul>
    <% model.errors.full_messages.each do |message| %>
      <li><%= message %></li>
    <% end %>
  </ul>
<% end %>

上のようなエラーメッセージを表示するパーシャル(部分的に再利用できるテンプレート)を作成しておき、以下のようにレンダリングしたりします。
この例では、layouts/errors.html.erbにエラー表示用のテンプレートを用意しています。

<%= form_for(@event) do |f|%>
<%= render 'layouts/errors', model: f.object %>

また、特定のフォームフィールドに対するエラー文のみを取得することもできます。
各入力フォームの真下や真上にエラーメッセージを表示したい場合などに使います。

<% if event.errors.include?(:name) %>
  <p><%= event.errors.full_messages_for(:name).first %></p>
<% end %>

エラーメッセージの日本語化

エラーメッセージを日本語に対応させるには、i18nのパッケージを使用します。
ちなみにi18nとは国際化を意味する英単語である「internationalization」を省略したものです。
18とはiとnの間に18個のアルファベットがあることを意味しています。

Gemfileに以下のrails-i18nを追加し、パッケージをインストールします。

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '3.0.2'

gem 'rails-i18n'

...
$ bundle install

次にconfig/application.rbの設定を書き換え、使用するロケール(言語)をjaに変更します。

...

module EventApp
  class Application < Rails::Application
    config.load_defaults 6.1
    config.i18n.default_locale = :ja    # この行を追加
  end
end

さらにconfig/localesフォルダの中にja.ymlを作成し、以下のようにしてカラム名を記述します。

ja:
  activerecord:
    attributes:
      event:
        name: "イベント名"
        hold_date: "開催日"

これでエラーメッセージが日本語化できました。
実際に日本語になっていることを確認してみましょう。

$ rails console

irb(main):001:0> event = Event.new
   (1.0ms)  SELECT sqlite_version(*)
=> #<Event:0x0000013bf42aac48 id: nil, name: nil, hold_date: nil, created_at: nil, updated_at: nil>

irb(main):002:0> event.valid?
=> false

irb(main):003:0> event.errors.full_messages
=> ["イベント名を入力してください"]

しっかりと日本語化できていることが確認できました!

まとめ

Webアプリケーションでユーザが入力されたデータが正しい形式であるかを確かめるには、バリデーションチェックを行います。
またデータに誤りがあれば、それをユーザに伝えて再度値を入力し直してもらう必要があります。

モデルにバリデーションを設定しておくことで、データベースに登録するデータを簡単に検証することができます。
バリデーション機能は頻繁に使用しますので、慣れておきましょう。

また、アプリケーションのユーザビリティ向上のためにエラーメッセージの日本語化も確実に身につけておきたいですね。

ひとりではなかなかプログラミングの学習が続かない、未経験だから不安が多い、という方はプログラミングスクールを利用してみるのも有効です。
本物のエンジニアに学ぶことで、時間がない方でも最短でスキルを身につけることができます。
現役エンジニアのパーソナルメンターからマンツーマンで学べる

お悩みの方は、まずは無料キャリアカウンセリングにお申込みください。

関連記事

この記事のタグ