Implist | 個人プロダクト開発を加速させるための理論と実装

Laravelで簡単にバリデーションを実装してエラーメッセージを表示してみる(日本語化も)


Laravel Eloquentでデータを追加・更新・削除を行う

前回の記事でデータの登録は出来るようになりましたが、入力をチェックするバリデーションが実装されていません。
ユーザーからの入力は必ずしも正しいものが来るとは限らず、場合によってはいたずらや不正な入力をされる可能性もあります。

Laravelではそれらの値をチェックして、実装者が意図するデータだけを通すというバリデーションの機能が充実しているので、それを簡単に見ていきましょう。
また、不正な値を入力された場合のエラーメッセージもカスタマイズできます。

controllerでバリデーションを記述して、エラー表示

まずはcontroller内でバリデーションを書いてみましょう。

app/Http/Controllers/UserController.php
public function create(Request $request)
{
$request->validate([
"name" => "required|min:2",
"age" => "required|integer|between:20,90"
]);

User::create([
"name" => $request->name,
"age" => $request->age,
]);

return redirect("users");
}

$request->validate()がバリデーションを実行してくれるメソッドです。

引数はkey value形式の連想配列となっており、

バリデーションしたいフィールド => ルール1|ルール2

というような記述となります。「|」つなぎでルールを記述することで複数条件のバリデーションが記述可能です。

今回、

  • nameは、必須入力、最低文字2文字
  • ageは、必須入力、整数、20〜90の数値

というバリデーションルールを設定してみました。
※ageにしているbetweenは注意が必要で、integerというルールを指定してないと桁数としてバリデーションされるようです。20桁〜90桁の数値というルールになってしまうので気をつけましょう。参考

またこれらのルールに合致しなかった場合は、後続の処理、今回であればUser::createは呼ばれずに処理が終了します。

バリデーションが通らなければ、当たり前ですが後続の処理は実行しないで欲しいですからね。

エラー表示

ではバリデーションが通らず、エラーとなった場合はどうユーザーにそれを伝えるのでしょう?
Laravelではエラーメッセージを簡単に表示することができます。

resources/views/users/index.blade.php
<div>
@if ($errors->any())
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
@endif
</div>

<form action="/users" method="post">
以下省略

このようにエラーがあるかないかは、$errors->any()で取得でき、存在する場合は$errors->all()で全てのバリデーションエラーが取得できるので、あとはそれを回して表示してあげるだけです。

例えば名前を「a」、年齢を「100」と入力したとすると、

このようにエラーが表示されます。
今はエラーですが、後ほど日本語にカスタマイズしていきます。

エラーとなった時に、入力した値をフォームに保持させる

上記の例だとバリデーションエラーとなった場合、エラーは表示されますが自分が入力した値が消されてしまいました。

せっかく入力したのに消されてはユーザビリティが悪いですね。
間違っている入力にせよ、そこから修正できるように入力値は保持させておくのが良いです。

それにはLaravelのヘルパ関数のoldというものを使用します。

resources/views/users/index.blade.php
<form action="/users" method="post">
{{ csrf_field() }}
<div>
<label>名前</label>
<input type="text" name="name" value="{{ old('name') }}"/>
</div>
<div>
<label>年齢</label>
<input type="number" name="age" value="{{ old('age') }}"/>
<input type="submit" value="登録"/>
</div>
</form>

nameとageのvalueにそれぞれ{{ old('name' ) }}{{ old('age') }}という記述を追加しました。
Laravelが一時的に入力データを保持しており、oldを使うとその値が取得され、valueにセットされるといった流れになっています。

実際にバリデーションエラーとなるように入力してみると、入力した値は保持されたままエラーメッセージが表示されているのが確認できます。

更新時のバリデーションと値保持

値の新規登録時にバリデーションをつけ、エラーメッセージを表示したり入力値の値保持を行ったりはできるようになりましたが、更新の場合も試してみましょう。

ルールとしては新規登録時と同じにすることにします。

app/Http/Controllers/UserController.php
public function update(Request $request)
{
$request->validate([
"name" => "required|min:2",
"age" => "required|integer|between:20,90"
]);

$user = User::find($request->userId);
$user->update([
"name" => $request->name,
"age" => $request->age,
]);

return redirect("users");
}

このバリデーションは新規登録時と同じですね。

次はviewですが、ここが若干変わります。

resources/views/users/show.blade.php
<div>
@if ($errors->any())
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
@endif
</div>

<form action="/users" method="post">
@method('PUT')
{{ csrf_field() }}
<input type="hidden" name="userId" value="{{ $user->id }}"/>
<div>
<label>名前</label>
<input type="text" name="name" value="{{ old('name', $user->name) }}"/>
</div>
<div>
<label>年齢</label>
<input type="number" name="age" value="{{ old('age', $user->age) }}"/>
<input type="submit" value="更新"/>
</div>
</form>

エラーメッセージを表示するところは新規登録時と同じですが、入力した値を保持するところが変わっています。

ヘルパ関数であるoldに2つ目の引数を与えています。

{{ old('name', $user->name) }}

これの意味するところは、

  • 第1引数: バリデーションエラー発生時に入力した値
  • 第2引数: デフォルト値(今回であればDBに保存されている値)

です。
つまりデフォルト値として、DBに保存されている値が設定されているので、
詳細ページを開いた時はDBに保存されている値が表示され、
更新してバリデーションエラーとなった場合は新しく入力した値が表示されるといった具合です。

詳しくはこちらを参照ください

エラーが発生した場合でも、画面をリロードするとまたデフォルト値としてDBに保存されている値が表示されます。

実際にバリデーションエラーを発生させてみると、ちゃんと新しく入力した値が表示されていますね。

バリデーションをまとめて簡単に書けるフォームリクエスト

先程までバリデーションのルールをcontrollerに記述してきましたが、基本的にはそのようなことはしません。
なぜならcontrollerがごちゃごちゃするからです。また新規登録時、更新時に同じバリデーションのルールなのに、同じことを2回書くのもあまり良くありません。

バリデーションはバリデーションとして違うファイルで定義するのが良いです。この時使えるのがフォームリクエストです。

まずはapp/Http配下にRequestsディレクトリを作ります。
そしてその中に以下ファイルを作成してください。

app/Http/Requests/UserRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class UserRequest extends FormRequest
{
public function rules()
{
return [
"name" => "required|min:2",
"age" => "required|integer|between:20,90"
];
}
}

詳しい説明は省きますが、先程controllerに記述していたルールがそのまま、このクラスのrules()メソッドに記述されています。

このようにRequestsディレクトリ配下をバリデーションのルールを定義する場所とすると、見通しがよくなります。必要であれば更にディレクトリを作成してもいいでしょう。

フォームリクエストのファイルを作成するだけだと何もおこらないので、次はcontrollerを変更していきます。

app/Http/Controllers/UserController.php
<?php

namespace App\Http\Controllers;

use App\Http\Requests\UserRequest; // 追加
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\View\View;

class UserController extends Controller
{
public function create(UserRequest $request) // 変更
{
User::create([
"name" => $request->name,
"age" => $request->age,
]);

return redirect("users");
}
以下省略

createメソッド内のバリデーションはなくなっていますね。
その代わりに、public function create(UserRequest $request)という記述が増えました。

ここで先程作成したUserRequestクラスを指定しています。
このように記述するだけで、バリデーションを実行してくれるんです。かなり便利ですよね。

Laravelの公式ドキュメントにも以下のように記述されています。

どのようにバリデーションルールを実行するのでしょうか?必要なのはコントローラのメソッドで、このリクエストをタイプヒントで指定することです。やって来たフォームリクエストはコントローラメソッドが呼び出される前にバリデーションを行います。

この状態でバリデーションエラーを発生されるように新規登録をすると、

ちゃんとバリデーションが効いているのが分かります。

更新時も、新規作成時のメソッドと同様の記述をすれば、同じくバリデーションが適用されます。

public function update(UserRequest $request)
{
$user = User::find($request->userId);
$user->update([
"name" => $request->name,
"age" => $request->age,
]);

return redirect("users");
}

これで、以下ができるようになりました。

  • バリデーションの適用
  • バリデーションエラーの表示
  • バリデーションロジックの共通化

最後にバリデーションエラーメッセージの日本語化を行っていきましょう。

バリデーションエラーメッセージの日本語化

バリデーションエラーメッセージを日本語するにあたって必要なファイルは3つだけです。

  1. バリデーションエラーメッセージのテンプレートファイル
  2. アプリケーション言語変更ファイル
  3. フォームリクエストファイル(先程作ったバリデーションを記述したクラス)

テンプレートファイル

まずはエラーメッセージが記述されているテンプレートファイルを見てみましょう。
プロジェクト直下のlang/enというディレクトリ配下にvalidation.phpというファイルがあります。

このファイルに記述されているのは、色々なバリデーションエラーが発生した場合、どういうメッセージを出すかというテンプレートになります。

ユーザーの新規登録時や更新時にはrequiredというルールを記述しましたが、このルールにマッチしなかった場合は(バリデーションエラーとなった場合)は、上記画像のようなエラーメッセージが表示されます。

これのことですね。
:attributeというのは、入力した箇所の属性名で、ここではnameと変換されているので、名前のバリデーションエラーになったんだと分かります。

このファイルの内容を変更すればエラーメッセージは変わるのですが、このファイルは変更しないほうが良いです。

なぜかと言うと、このvalidaiton.phpがあるのはlang/enというディレクトリの中で、このenはenglishを表しており英語版のバリデーションメッセージであるからです。

ほとんどの人が英語版のアプリケーションを作っているわけではないと思うので、日本語用のテンプレートを作成したほうが良いでしょう。enのまま日本語に変更していっても使えるは使えるのですが、正しいものを正しいところに配置するほうが良いので、日本語用テンプレートを作成しましょう。

と入っても簡単で、lang配下にjaディレクトリを作成して、validation.phpをenからコピーしましょう。
まだ英語のままなので、以下のように日本語に変更してみます。

全部をいきなり日本語化するのは大変なので、最初のうちは必要になったときに変更するくらいで良いでしょう。
また公式ドキュメントに日本語用のバリデーションテンプレートもあるようなので、それを初めから使ってもいいかもしれません(日本語訳がおかしい場合があります)。

アプリケーション言語変更ファイル

ja用のディレクトリを作成した場合は、実はそのままだとそのファイルは認識されずenのままになってしまっています。

そこで、config/app.phpのlocaleというところをenからjaに変更します。

'locale' => 'ja',

こうすることで、jaディレクトリ配下のvalidation.phpが読まれるようになります。

ここで、試しに未入力で新規登録を行ってみると

ちゃんと日本語になっているのが確認できました。
ただname、ageが英語のままです。これも日本語にしていきましょう。

フォームリクエストファイルの修正して属性名を日本語化する

以下の:attribute(属性名)の部分を任意の日本語に変えていきます。

テンプレートファイルは変更せずに、先程作成したフォームリクエストのクラスを変更していきます。

app/Http/Requests/UserRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class UserRequest extends FormRequest
{
public function rules()
{
return [
"name" => "required|min:2",
"age" => "required|integer|between:20,90"
];
}

public function attributes()
{
return [
"name" => "名前",
"age" => "年齢",
];
}
}

追加したのは、attributes()というメソッドのみで、

  • nameを名前
  • ageを年齢

となるように修正しました。このようにすることで、バリデーションエラーを発生させてみると、

属性名もそれぞれ日本語に置き換えられ、すべてが日本語のバリデーションエラーメッセージとなりました。

 

これでバリデーションの実装と、エラーメッセージの表示が出来るようになったと思います。
バリデーションルールは他にもたくさんありますし、新たにカスタムバリデーションという独自のバリデーションを作ることも出来るので、自身のアプリケーションで必要なれば適宜実装していってください。

これで、Laravel超入門編は完結です。おつかれさまでした。

Laravel Eloquentでデータを追加・更新・削除を行う