入門者のためのLaravelのバリデーションとエラーメッセージ

目次
バリデーション(validation)とは
入力フォームで入力した値をチェックすることをバリデーションと呼びます。例えばECから商品を初めて購入した場合は必ず住所の入力が必要となります。入力した郵便番号に漏れがないか数字であるかまた郵便番号の桁数が7桁になっているかなどチェックすることがバリデーションです。
基本的なバリデーションの動作についてはLaravel5, Laravel6, Laravel7, Laravel8で共通です。またバリデーションという言葉はLaravel特有のものではありません他の言語やフレームワークでも利用されるものです。
本文書を読み終えるとバリデーションの下記の理解を深めることができます。
- バリデーションとは何か
- Laravelソースコードで実際のバリデーションの中身を確認
- バリデーションの方法
- バリデーションエラーの表示と表示場所の設定
- バリデーションエラーの日本語化
- カスタムバリデーションの作成方法
バリデーションって難しいの??
バリデーションという言葉に聴き慣れていない人であればバリデーションは非常に難しく複雑なものだと思う人もいるかもしれませんが処理は非常にシンプルです。まず入力した値が入るフィールドに対してルールを設定します。入力した値がそのルールにパスするか失敗するかチェックをします。

実際にLaravelのソースコード(ValidatesAttributes.php)を確認してルールがどれほどシンプルかを確認していきます。最もシンプルなルールの一つである値が数値がどうかチェックするNumericというものがあります。実際のチェック方法は以下の通りです。ルールといってもシンプルなものはこれだけで終わります。これなら入門者の人でも自作できるレベルです。
public function validateNumeric($attribute, $value)
{
return is_numeric($value);
}

もしルールにパスしなかった場合(Numericであれば数値以外のものが入力された場合)は、ルール名を利用してメッセージ用のファイルlang¥en¥validation.phpファイルにその名前のルールがあるかチェックを行いもしある場合はそれをエラーメッセージを入れる入れ物であるMessageBagに登録しているだけです。
'numeric' => 'The :attribute must be a number.',
【補足】
入力値がアルファベットかどうかチェックを行いたい時にalphaというルールを利用して設定を行うと日本語でもルールにパスします。自分がイメージするルールの単語と実際の処理内容が異なっている場合があるのでそのような時はぜひValidatesAttributes.phpファイルを確認してみてください。
動作確認環境の準備
バリデーションがどういうものなのかイメージがつかめたと思うので、実際にLaravelを動作しながら理解を深めていきます。バリデーションの基本設定、バリデーションによるエラーメッセージの表示場所を確認するため、動作確認環境の構築を行います。
動作検証のデータベースの場合はsqliteデータベース環境を構築すると簡単です。
postsテーブルの準備
動作確認を行うためにpostsテーブルを作成します。postsテーブルは、titleとbodyの2つ列から構成されます。
Postモデルとマイグレーションファイルの作成を行います。Postモデルと同時にマイグレーションファイルも作成するためphp artisan make:modelコマンドに-mオプションをつけて実行します。
mac$ php artisan make:model Post -m
Model created successfully.
Created Migration: 2019_01_01_053048_create_posts_table
モデルPost.phpファイルはappディレクトリの下にマイグレーションファイルはdatabase/migrationsディレクトリの下に作成されます。
postsテーブルを作成するマイグレーションファイルにtitle, bodyの列情報を追加します。
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePostsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('body');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('posts');
}
}
postsテーブルのtitle, body列の挿入が行えるようにPost.phpファイルを更新します。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = [
'title', 'body',
];
}
postsテーブルの準備ができたので、php artisan migrateコマンドでpostsテーブルを作成します。
mac$ php artisan migrate
Migrating: 2019_01_01_053048_create_posts_table
Migrated: 2019_01_01_053048_create_posts_table
ルーティングの設定
web.phpに以下のルーティングの設定を行います。
Route::get('post/create', 'PostController@create');
Route::post('post', 'PostController@store');
コントローラーの作成
php artisan make:controllerコマンドを使ってPostController.phpの作成を行います。
mac$ php artisan make:controller PostController
Controller created successfully.
ルーティングファイルweb.phpで追加したcreateメソッドとstoreメソッドを記述します。storeメソッドの中にValidationのロジックを記述します。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function create()
{
return view('post.create');
}
public function store(Request $request)
{
// ここにValidationを行うロジックを記述する
}
}
ビューの作成
入力フォームを表示させるためのビューの作成を行います。views/postディレクトリの下にcreate.blade.phpを行います。

create.blade.phpファイルの保存場所
ビューファイルの中身は下記の通りです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Create Post</title>
</head>
<body>
<form method="POST" action="/post" enctype="multipart/form-data">
{{ csrf_field() }}
<p>name:<br>
<input type="text" name="title" value="">
</p>
<p>body:<br>
<textarea name="body" cols="25" rows="5"></textarea>
</p>
<button type="submit">登録する</button>
</form>
</body>
</html>
/post/createにアクセスすると作成した入力フォームが表示されます。

入力フォーム
バリデーションのメソッドとルール
validateメソッドについて
入力フォームから入力した値のチェックを行うためにPostControllerのstoreメソッド内にバリデーションを追加します。バリデーションはvalidateメソッドを用いて行い、テーブルの各列に対してルールを設定し、入力値のチェックを行います。ルールは各列に対して、複数設定することもできます。複数のルールを設定する場合は、区切り文字”|”を使って分けます。
validateメソッドの記述方法は下記となります。

validateメソッド

postsテーブルのtitle列には、入力が必須となるルールrequiredと入力した行と重複の確認を行うルールuniqueを設定します。body列には、ルールrequiredのみ設定しています。
public function store(Request $request)
{
$request->validate([
'title' => 'required|unique:posts',
'body' => 'required',
]);
}
// ルールは配列でも可能
public function store(Request $request)
{
$request->validate([
'title' => ['required','unique:posts'],
'body' =>['required'],
]);
}
validateメソッドを使用してバリデーションに失敗した場合は自動で入力フォーム画面に移動します。
バリデーションルール
required, unique以外にもたくさんのバリデーションルールがあります。Laravelのドキュメントを確認しながら、必要なルールを見つけ、適用していきます。

バリデーションルール一覧
Concerns¥ValidatesAttributes.phpファイルを確認してください。(Laravel8の場合)

ビューへのエラーメッセージ要素の追加
validationに失敗した場合のエラーメッセージを表示できるように入力フォームのビューであるcreate.blade.phpにメッセージを表示できる要素を追加します。
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
変数$errorsは、バリデーションを実行するしないに関わらずビューの中で利用することが可能な変数です。
$errors->any()は、変数$errorsの中にエラーメッセージがあるかどうかチェックを行うメソッドです。エラーメッセージがあればtrue、なければfalseを戻します。
$errors->all()にはバリデーションの中のチェックで発生したエラー情報が配列として保持しています。foreachを使用することで1つ1つのメッセージを取り出すことができます。
エラーメッセージの表示
バリデーションの動作確認を行うため入力フォームには、何も入力せず登録ボタンをクリックします。

入力フォーム
titleにもbodyにも入力を行なっていなかったため、field is requiredのメッセージが表示されます。

エラーメッセージの表示

カスタマイズしたエラーメッセージの設定
初期設定では、エラーメッセージは英語で表示されています。メッセージをカスタマイズすることも可能です。以下のようにvalidateメソッドに表示したいメッセージの配列を追加します。
public function store(Request $request)
{
$request->validate([
'title' => 'required|unique:posts',
'body' => 'required',
],
[
'title.required' => 'タイトルは必須です。',
'body.required' => 'bodyは必須項目です。',
]);
}
再度入力フォームに何も入力せず、登録ボタンを押すと追加したカスタマイズしたメッセージが表示されます。

カスタマイズしたエラーメッセージ
エラーメッセージの日本語化
エラーメッセージをカスタマイズして日本語化する方法の確認を行いましたが、validation毎に個別に設定を行うのは非効率のため、一括で日本語化を行う方法を確認します。日本語化までの流れは下記の通りです。
- config/app.phpファイルでロケールの変更
- resources/langディレクトリにjaディレクトリ作成
- resources/lang/en/validation.phpファイルをjaディレクトリに複製
- jaディレクトリ内のvalidation.phpファイルの更新
validationのエラーメッセージの一覧が登録されているファイルは、resources/lang/en/validation.phpに登録されています。
<?php
return [
/*
|--------------------------------------------------------------------------
| Validation Language Lines
|--------------------------------------------------------------------------
|
| The following language lines contain the default error messages used by
| the validator class. Some of these rules have multiple versions such
| as the size rules. Feel free to tweak each of these messages here.
|
*/
'accepted' => 'The :attribute must be accepted.',
'active_url' => 'The :attribute is not a valid URL.',
・
'required' => 'The :attribute field is required.',
・
ロケールの設定
初期設定では、ロケールの設定が”en”になっているために”ja”に変更する必要があります。設定ファイルは、Laravelインストールディレクトリのconfig/app.phpで行います。下記のように’en’を’ja’に変更します。
/*
|--------------------------------------------------------------------------
| Application Locale Configuration
|--------------------------------------------------------------------------
|
| The application locale determines the default locale that will be used
| by the translation service provider. You are free to set this value
| to any of the locales which will be supported by the application.
|
*/
'locale' => 'ja',
validation.phpファイル複製
app.phpファイルでロケールの作成完了後、resources/langディレクトリにjaディレクトリを作成し、enディレクトリに保存されているvalidation.phpファイルをjaディレクトリの下に保存します。保存したvalidation.phpの中にあるrequiredを日本語に変更します。

validationメッセージを日本語化
再度入力フォームに何も入力せず、ja/validation.phpファイルで日本語化したメッセージが表示されます。

validation.phpで日本語化したエラーメッセージ
requiredのみ日本語化を行いましたが、他のメッセージも日本語に変更すれば、すべてのエラーメッセージを日本語化することが可能です。
エラーメッセージの表示場所
エラーメッセージに取得に関するメソッド
入力した値毎にエラーメッセージを表示することが可能です。下記の方法で入力した項目のエラーメッセージを取り出すことが可能です。

firstメソッドでエラーメッセージを取得
firstメソッドでは、項目のvalidationでエラーになったルールの1つしか取得することができません。すべてのルールのエラーメッセージを取得するためには、getメソッドを使用します。

getメソッドですべてのエラーを取得

各項目にエラーメッセージが入っているかどうかをチェックするhasメソッドもあります。

エラーメッセージを持っているかの確認
項目ごとのエラーメッセージ
hasメソッドとfirstメソッドを利用してエラーメッセージを表示します。
@if($errors->has('body'))
{{ $errors->first('body') }}
@endif
hasメソッドとgetメソッドを利用してエラーメッセージを表示します。
@if($errors->has('title'))
@foreach($errors->get('title') as $message)
{{ $message }}
@endforeach
@endif
項目別にエラーメッセージを表示
これまでのエラーメッセージの設定を使って、エラーメッセージを表示場所を項目の下に表示させます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Create Post</title>
</head>
<body>
<form method="POST" action="/post" enctype="multipart/form-data">
{{ csrf_field() }}
<p>title:<br>
@if($errors->has('title'))
@foreach($errors->get('title') as $message)
{{ $message }}<br>
@endforeach
@endif
<input type="text" name="title" value="">
</p>
<p>body:<br>
@if($errors->has('body'))
{{ $errors->first('body') }}<br>
@endif
<textarea name="body" cols="25" rows="5"></textarea>
</p>
<button type="submit">登録する</button>
</form>
</body>
</html>
再度入力フォームに何も入力せず、登録ボタンを押すと入力項目の下にエラーメッセージが表示されます。

エラーメッセージの表示場所を変更

フィールド名を変更する
項目別にエラーメッセージを表示することができましたが、titleは必須ですはテーブル作成時の列名(フィールド名)なのでこれを日本語のタイトルに変更したい場合もあるかと思います。
その場合は、validator.phpファイルのattributesに追加することで変更することができます。
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap our attribute placeholder
| with something more reader friendly such as "E-Mail Address" instead
| of "email". This simply helps us make our message more expressive.
|
*/
'attributes' => ['title' => 'タイトル'],
カスタムバリデーション
Laravelにはデフォルトでさまざまなバリデーションが準備されていますが独自のバリデーションを作成したいということもあるかもしれません。その場合はカスタムバリデーションルールを利用することで対応することができます。
ルールファイルの作成
入力フォームで住所を入力した場合の郵便番号のカスタムバリエーションを作成してみましょう。
$ php artisan make:rule Zipcode
Rule created successfully.
app\Rulesフォルダの下にZipcode.phpファイルが作成されます。中身はpassesとmessageメソッドが入っています。passesメソッドは引数に$attributeと$valueを受け取って$valueの値が条件を満たすかチェックを行います。今回は$valueの値が郵便番号のフォーマットの条件を満たせばtrueを戻し、満たさなければfalseを戻します。もし、条件を持たさなかった場合は、messageメソッドに記述した内容がエラーメッセージとして戻されます。
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class Zipcode implements Rule
{
public function __construct()
{
//
}
public function passes($attribute, $value)
{
//
}
public function message()
{
return 'The validation error message.';
}
}
利用方法
追加したルールを利用する場合は下記のように記述します。
略
use App\Rules\Zipcode;
//略
public function store(Request $request){
$input = $request->all();
Validator::make($input, [
'zipcode' => ['required',new Zipcode],
])->validate();
この状態で$attributeと$valueを確認すると$attributeにはzipcode, $valueには入力フォームで入力した値が表示されます。
バリデーション条件の設定
郵便番号の形式は3桁-4桁なので正規表現を利用してチェックを行います。
public function passes($attribute, $value)
{
return preg_match('/^\d{3}[-]\d{4}$/', $value);
}
実際に入力した郵便番号でチェックすると163-8001ではバリデーションをパスしますが、1638001や郵便の桁数の不足するものまた数字以外を入力するとmessage関数のThe validation error messageがエラーで戻されます。
メッセージの変更方法
メッセージを変更したい場合は下記のように直接messageメソッドを変更することも可能です。
public function message()
{
return '郵便番号の書式に誤りがあります';
}
属性の情報を表示したい場合は、:attributeを利用することでメッセージに属性名を表示させることができます。今回はzipcodeと表示されます。
public function message()
{
return ':attributeの書式に誤りがあります';
}
エラー内容を記述したvalidation.phpファイルに記述された内容のメッセージを表示させたい場合は、trans関数を利用することができます。意図的にdateルールのメッセージを利用した場合は下記のように設定します。
public function message()
{
return trans('validation.date');
}
メッセージは”zipcode is not a valid date.”と表示されます。
zipcodeというメッセージをValication.phpファイルに追加することも可能です。validation.phpファイルを開いて下記を追加します。
//略
'uuid' => 'The :attribute must be a valid UUID.',
'zipcode' => 'エラー',
//略
ルールファイルのZipcode.phpファイルのメッセージ関数を以下のように変更します。
public function message()
{
return trans('validation.zipcode');
}
バリデーションに失敗するとメッセージで”エラー”が表示されます。
アプリケーションを構築する際にどのルールを利用するか個別に吟味する必要がありますが本文書を読み終えたのでLaravelのバリデーションの最初の一歩を踏み出すことができます。Laravelのドキュメンテーションを見ると今回の説明はLaravelバリデーションを一部にすぎません。本文書をベースにバリデーションの理解をさらに深めていってください。