フォーム処理時にバリデーションエラーが発生した場合の実装【日本語化・スタイル調整】

はじめに

フォームから送られるパラメータがモデルのバリデーションに引っかかった場合、エラー内容をビューに表示する必要があります。
Railsではそのための仕組みがあらかじめ用意されているので、比較的簡単に実装することが出来ます。
ただし、一部そのままでは使いにくい箇所があるので、そのあたりも含めてエラー時の処理をまとめたいと思います。

実装

なにはともあれエラー処理の基本を実装していきます。記事の新規作成時の処理をサンプルコードとしています。

コントローラー

ビューから送られてきたタイトルと本文を使ってレコードを新規作成します。
新規作成するときにモデルのバリデーションに引っかかった場合、レコードは作成されずエラー内容とともにビューに戻されます。

class ArticlesController < ApplicationController
  def create
    @article = Article.new(articles_params)
    if @article.save
      redirect_to articles_path
    else
      render :new
    end
  end

  private
    def articles_params
      params.require(:article).permit(:title, :content)
    end
end

余談ですが、エラー時にredirect_toではなくrenderを使っている理由は、renderはページ遷移時にコントローラーを介さないため、エラー内容を含んだパラメータをそのままビューに渡せるためです。
redirect_toを使ってしまうと、newメソッドが実行され、パラメータが上書きされることによりエラー内容がビューで参照できなくなってしまいます。

モデル

タイトルと本文に必須のバリデーションを実装します。

class Article < ApplicationRecord
  validates :title, presence: true
  validates :content, presence: true
end

ビュー

Rails5以降ではフォームの生成にform_withヘルパーメソッドの使用が推奨されています。form_forform_tagも使えますが、将来廃止される可能性もあるので使わないほうが無難でしょう。
form_withはデフォルトで非同期で処理を行うので、local: trueオプションを忘れないようにします。これを忘れると、コントローラーからエラー内容を受け取ることが出来ません。

_form_html.erb

<%= form_with model: @article, local: true do |form| %>
  <%= render 'shared/errors', { target: @article } %>
  ...
<% end %>

shared/_errors.html.erb

<% if target.errors.any? %>
  <div class="text-danger" role="alert">
    <h3>エラー項目があります</h3>
    <ul>
      <% target.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
    </ul>
  </div>
<% end %>

以上で基本的なエラー時の処理が実装できました。

日本語化

エラー内容はデフォルトでは英語でしか表示されないので日本語化します。

Gem追加

Gemfileに以下を追記してbundle installを行います。

gem 'rails-i18n'

application.rb変更

config/application.rbに以下を追記します。

...

module AppName
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.0

    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration can go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded after loading
    # the framework and any gems in your application.

    # 以下を追記
    config.time_zone = 'Tokyo'
    config.active_record.default_timezone = :local
    config.i18n.default_locale = :ja
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
  end
end

日本語化ファイル作成

config/locales/ja.ymlを作成し、以下を記述します。

ja:
  activerecord:
    attributes:
      article:
        title: タイトル
        content: 本文

以上でエラー内容の日本語化が出来ました。

スタイルを整える

エラーが発生しビューに戻ってくると、エラー項目を囲む.field_with_errorsというスタイルが自動的に付与されます。

...
  <div class="form-group row">
    <%= form.label :title, 'タイトル(必須)', class: 'col-12 col-sm-2 col-form-label' %>
    <div class="col-12 col-sm-10">
      <%= form.text_field :title, class: 'form-control', placeholder: 'タイトル' %>
    </div>
  </div>
...

エラーが発生すると以下のようになります。

...
  <div class="form-group row">
    <div class="field_with_errors">
      <%= form.label :title, 'タイトル(必須)', class: 'col-12 col-sm-2 col-form-label' %>
    </div>
    <div class="col-12 col-sm-10">
      <div class="field_with_errors">
        <%= form.text_field :title, class: 'form-control', placeholder: 'タイトル' %>
      </div>
    </div>
  </div>
...

Bootstrapを使用していると、自動付与された.field_with_errorsによりスタイルが崩れてしまいます。
これを回避するにはスタイルを上書きする必要があります。
app/assets/stylesheets/application.scssに以下を追記します。

...
.field_with_errors {
  display: contents;
  input, textarea {
    @extend .is-invalid;
  }
}
...

.field_with_errorsの表示を変更するとともに、内包するinputtextarea.is-invalidのスタイルを適用させています。
この書き方はBootstrapを使っている場合に限られますのでご了承ください。

まとめ

エラー時の処理はRailsチュートリアルにも書いてある基本的な機能ですが、忘れやすいところですし、それだけでは不十分だったのでまとめてみました。

関連記事

【Rails】Paranoiaを使用した論理削除(ソフトデリート)
# はじめに Paranoiaは、Railsアプリケーションで論理削除(ソフトデリート)を実現するためのGemです。 論理削除は、データベースのレコードを物理的に削除するのではなく、削除フラグを設定することで「削除済み」とみなす方法です。こ [...]
2024年7月20日 21:33
【Rails】activerecord-multi-tenantを使用したマルチテナントアプリケーションの作成
# はじめに マルチテナントアプリケーションでは、複数の顧客(テナント)が同じアプリケーションを利用するため、データの分離が必要です。 activerecord-multi-tenantは、このようなマルチテナント環境をサポートするための便 [...]
2024年7月18日 16:50
【Rails】RubyとRailsにおけるattr_reader, attr_writer, attr_accessorの概念と使用方法
# はじめに RubyとRailsの開発において、`attr_reader`,`attr_writer`,`attr_accessor`は非常に便利なメソッドです。これらは、クラス内でインスタンス変数に対するゲッターおよびセッターメソッドを簡単に [...]
2024年7月17日 18:11
【Rails】RubyとRailsにおけるyieldの概念と使用方法
# はじめに RubyとRailsにおける`yield`は、メソッドやテンプレートの中で動的にコードブロックを実行する能力を提供し、これによってコードの再利用性と拡張性が大幅に向上します。本記事では、RubyとRailsにおける`yield`の概 [...]
2024年7月17日 13:15
【Rails】AASMを使用してオブジェクトの状態遷移を効率的に管理
# はじめに Railsアプリケーションにおいて、オブジェクトの状態管理は重要な課題の一つです。AASM (Acts As State Machine) gemは、複雑な状態遷移を効率的に管理します。本記事では、AASMの基本的な使い方を解説して [...]
2024年7月16日 18:00
【Rails】RSpec + Swagger + rswagでアプリケーションのAPIをテストおよびドキュメント化する方法
# はじめに Railsアプリケーションの開発において、APIのテストとドキュメント化は重要な要素です。 RSpecはテストフレームワークとして広く利用されており、SwaggerはAPIの設計とドキュメント化を支援します。これらを統合するr [...]
2024年7月16日 14:27
【Rails】mailcatcherを使用して開発環境でメール送信をテストする方法
# はじめに mailcatcherは、開発環境でのメール送信をキャプチャするためのツールです。ローカルで送信されたメールをブラウザ上で簡単に確認できるようにします。mailcatcherをRailsアプリケーションで使用する方法について説明しま [...]
2024年7月15日 16:37
【Rails】impressionistを使用してページビューやクリック数を追跡する方法
# はじめに impressionist Gemを使用してRailsアプリケーションでページビューやクリック数を追跡する方法について説明します。 # 実装方法 ## impressionist Gemのインストール まず、impre [...]
2024年7月15日 14:18