RitoLabo

Laravelのvalidationメソッドでバリデーションを実装する入門編(日本語エラーメッセージ付き)

  • 公開:
  • 更新:
  • カテゴリ: PHP Laravel
  • タグ: PHP,Laravel,artisan,5.5,Beginner,validation,Form,5.6

LaravelなどのPHPフレームワークを使ったWebアプリケーション開発において、アンケートや問い合わせ、会員登録、メッセージなど、フォームを使った機能開発要件はポピュラーではないでしょうか。

そんな中で、送信されるデータを安全かつ健全なデータとしてデータベースへ格納、もしくは何らかの手段で蓄積していく場合に、バリデーション(妥当性確認)は避けて通れない道であると言えます。

バリデーションを行わなければ、作り手の意図しない形でデータが送信されてしまいエラーを引き起こすだけでなく、悪意のあるユーザからのSQLインジェクションなどの攻撃の被害を受けてしまう危険性も高くなります。

Laravelではフォーム処理の剛健性高めるべく、validationメソッドが用意されています。これを使う事で、私たちは簡単に複雑なバリデーションを実装する事が出来ます。

今回はフォームのPOST送信から、validationメソッドを使ったバリデーションとエラーメッセージの表示までを実装していきます。

アジェンダ
  1. 開発環境
  2. フォームまわり準備
    1. ルーティング設定
    2. コントローラ作成
    3. ビュー(フォーム)
  3. バリデーションの実装
    1. 自動リダイレクトは行わないバリデーション
  4. バリデーションエラーメッセージの実装
  5. 動作確認
  6. エラーメッセージを日本語化する
    1. ロケール設定
    2. 日本語ファイルの設定

開発環境

今回の開発環境は以下の通りです。

  • Linux CentOS 7
  • Apache 2.4
  • PHP 7.1
  • Laravel 5.x

Laravelのバージョンに関しては、5.7/5.6/5.5で動作確認済みです。

XAMPP環境でも、artisanコマンドが叩ければ同手順で進めていけます。最悪叩けなくても、ファイル生成時に手動で作成すればOKです。

Laravelのルートディレクトリを「laravel」とします。

フォームまわり準備

バリデーションを行う為に、まずはフォームなど一連の流れを作成していきます。

ルーティング設定

まずはルーティングを設定します。laravel/routes/web.phpを開き、下記を追記します。

// アクセス時のルーティング
Route::get('sample/vali', 'ValiController@index');
// フォーム送信時のルーティング
Route::post('sample/vali', 'ValiController@receiveData');

http://YOURDOMAIN/sample/valiにアクセスしたらフォームのページを表示し、POST送信を行ったらバリデーションを行う流れです。

コントローラ作成

次に、コントローラを作成します。

laravelルートディレクトリへ移動し、以下のartisanコマンドを叩いてコントローラを生成します。

# laravelルートディレクトリへ移動
cd /path/to/laravel

# artisanコマンドでValiコントローラを生成する
php artisan make:controller ValiController

# 実行結果
[demo@localhost laravel]$ php artisan make:controller ValiController
Controller created successfully.

laravel/app/Http/Controllers/配下にValiController.phpが生成されます。

laravel
├ app
│ ├─ Http
│ │   ├─ Controllers
│ │   │   ├─
ValiController.php

生成したコントローラに、ビューへの記述を行います。

laravel/app/Http/Controllers/ValiController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ValiController extends Controller
{
public function index()
{
return view('sample_vali');
}
}

ビュー(フォーム)

laravel/resources/views/配下にsample_vali.blade.phpを作成し、以下を記述します。

laravel/resources/views/sample_vali.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>バリデーションのサンプル</title>
<style>
h1 { font-size: 16px; }
.form_wrap { padding: 10px; }
</style>
</head>
<body>
<h1>サンプルフォーム</h1>
<div class="form_wrap">
<form method="post" action="" enctype="multipart/form-data">
{{ csrf_field() }}
<table>
<tr>
<td>名前</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>性別</td>
<td>
<input type="radio" name="gender" value="1">
<input type="radio" name="gender" value="2">
</td>
</tr>
<tr>
<td>年齢</td>
<td><input type="text" name="age"></td>
</tr>
<tr>
<td>ファイル</td>
<td><input type="file" name="file"></td>
</tr>
<tr>
<td>コメント</td>
<td><textarea name="comment"></textarea></td>
</tr>
</table>
<p>
<input type="submit" value="送信">
</p>
</form>
</div>
</body>
</html>

ここまで用意できたら、一度ブラウザからhttp://YOURDOMAIN/sample/valiにアクセスしてみます。

サンプルフォームページ

簡単にではありますがフォームページをサンプルで作ったので、ここにバリデーションを実装していきます。

バリデーションの実装

それではこれからバリデーションを実装していきます。

ValiControllerクラスにpostした際のreceiveData()アクションを以下のように記述していきます。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ValiController extends Controller
{
public function index()
{
return view('sample_vali');
}

public function receiveData(Request $request)
{
$request->validate([
'name' => 'required|string|max:255', // 必須・文字列・255文字以内
'gender' => 'required', // 必須
'age' => 'required|integer', // 必須・整数
'file' => 'required|file|image|max:10000', // 必須・アップロードに成功している・画像ファイルである・10MB以内である
'comment' => 'required', // 必須
]);

return view('sample_vali', ['status' => true]);
}
}

上から説明していきます。

public function receiveData(Request $request)

POSTデータを受け取るreceiveData()アクションに、Requestクラスを引数として渡します。

$request->validate([
'name' => 'required|string|max:255', // 必須・文字列・255文字以内
'gender' => 'required', // 必須
'age' => 'required|integer', // 必須・整数
'file' => 'required|file|image|max:10000', // 必須・アップロードに成功している・画像ファイルである・10MB以内である
'comment' => 'required', // 必須
]);

requestクラスのvalidate()メソッド内に、バリデーションルールを記述しています。

'フォームのname' => 'バリデーションルール',

の要領で記述します。

return view('sample_vali', ['status' => true]);

バリデーションを通過したら、sample_valiビューへ、status=TRUEとして渡します。

これで一連の流れは記述出来ました。意外とスッキリしていると思うかもしれません。

ここで特筆すべきは、「バリデーションに通らなかった場合は、自動リダイレクトがかかる」という事です。

Laravelでは、バリデーションに通らなかった場合に、エラーレスポンス(メッセージ)を持って自動的にフォームのページへ遷移する。という仕様になっています。

なので、基本形である上記例では、バリデーション失敗時の記述を行う必要がありません。

逆にバリデーションに通った場合は、その次へ処理が続く。という流れになります。

なのでここでは、バリデーションに通った場合は、ビューを返す記述を行っています。

もっと実用的な記述を考えれば、validate()メソッドの後にデータベースへのデータ格納などを記述していく。みたいな流れになりますね。

自動リダイレクトは行わないバリデーション

バリデーションは行うが、エラー時は独自に処理を行いたいなど、自動リダイレクトを行いたくない場合もあるかもしれません。その時はvalidate()メソッドは使わずにValidatorファサードのmake()メソッドを使う事で、自動リダイレクトを伴わないバリデーションを実装する事も出来ます。その場合は、以下のようになります。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use Validator;

class ValiController extends Controller
{
public function receiveData(Request $request)
{
// バリデーションルール
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'gender' => 'required',
'age' => 'required|integer',
'file' => 'required|file|image|max:10000',
'comment' => 'required',
]);

// バリデーションエラーだった場合
if ($validator->fails()) {
return redirect('sample/error')
->withErrors($validator)
->withInput();
}

return view('sample_vali', ['status' => true]);
}
}

上から解説していきます。

use Validator;

Validatorクラスをuseします。

// バリデーションルール
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'gender' => 'required',
'age' => 'required|integer',
'file' => 'required|file|image|max:10000',
'comment' => 'required',
]);

Validatorクラスのmakeメソッドに、第一引数でPOST送信したデータ$request->all()を渡し、第二引数でバリデーションルールを渡しています。

// バリデーションエラーだった場合
if ($validator->fails()) {
return redirect('sample/error')
->withErrors($validator)
->withInput();
}

$validator->fails()でバリデーションエラーの場合はTRUEが返ってくるので、エラーの場合の処理を記述しています。

ここでは一例として、http://YOURDOMAIN/sample/errorへリダイレクトさせ、その際にwithErrors()メソッドでバリデーション情報を渡しています。

上記のようにすると自動リダイレクトはかからないので、自身の処理を実装する事が出来ます。

ちなみに、Validatorファサードのmake()メソッドを使って、さらに自動リダイレクトを行う場合は、以下のようにすると実現できます。

// バリデーションルール
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'gender' => 'required',
'age' => 'required|integer',
'file' => 'required|file|image|max:10000',
'comment' => 'required',
])->validate();

ひとつのバリエーションとして紹介しました。

では先に進みます。これから先は、自動リダイレクトを行う仕様に戻して話を進めていきます。

バリデーションエラーメッセージの実装

バリデーションの実装は行えたので、動作確認の前にエラーメッセージを表示する部分を追加します。

バリデーションエラー時に、エラーメッセージを持って元のページへ自動リダイレクトがかかると解説しましたが、もちろんそのままでは何も起こらないので、エラーを表示する部分を作ってあげる必要があります。

という事で、フォームのページへ、以下の記述を追記します。

laravel/resources/views/sample_vali.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>バリデーションのサンプル</title>
<style>
h1 { font-size: 16px; }
.form_wrap { padding: 10px; }
.errors {
width: 300px;
font-size: 12px;
color: #e95353;
border: 1px solid #e95353;
background-color: #f2dede;
}
.complete {
padding-left: 10px;
width: 290px;
font-size: 12px;
color: #2a88bd;
border: 1px solid #2a88bd;
background-color: #a6e1ec;
}
</style>
</head>
<body>
<h1>サンプルフォーム</h1>
@if ($errors->any())
<div class="errors">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@isset ($status)
<div class="complete">
<p>バリデーションを通過しました</p>
</div>
@endisset
<div class="form_wrap">
<form method="post" action="" enctype="multipart/form-data">
{{ csrf_field() }}
<table>
<tr>
<td>名前</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>性別</td>
<td>
<input type="radio" name="gender" value="1">
<input type="radio" name="gender" value="2">
</td>
</tr>
<tr>
<td>年齢</td>
<td><input type="text" name="age"></td>
</tr>
<tr>
<td>ファイル</td>
<td><input type="file" name="file"></td>
</tr>
<tr>
<td>コメント</td>
<td><textarea name="comment"></textarea></td>
</tr>
</table>
<p>
<input type="submit" value="送信">
</p>
</form>
</div>
</body>
</html>

ここで追加したのは、以下の3カ所です。

<style>
.
errors {
width: 300px;
font-size: 12px;
color: #e95353;
border: 1px solid #e95353;
background-color: #f2dede;
}
.
complete {
padding-left: 10px;
width: 290px;
font-size: 12px;
color: #2a88bd;
border: 1px solid #2a88bd;
background-color: #a6e1ec;
}
</
style>

エラーメッセージと完了メッセージ用のスタイルです。

@if ($errors->any())
<div class="errors">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif

エラーメッセージの表示部分です。バリデーションエラーの場合は$errorsに内容が格納されてくるので、$errors->any()でエラーの有無をチェックし、エラーがあれば、格納されている全てのエラーメッセージ$errors->all()をループして1つずつ表示しています。

@isset ($status)
<div class="complete">
<p>バリデーションを通過しました</p>
</div>
@endisset

バリデーション通過時に表示するメッセージです。コントローラでのバリデーション通過後に、statusをtrueとしてビューに渡していましたが、通常では$statusは存在していないので、$statusに値があればイコール成功という事で、成功メッセージを表示させています。

動作確認

これで一通りのロジックは組めたので、実際に操作して確認してみます。ブラウザからhttp://YOURDOMAIN/sample/valiへアクセスし、実際にフォームを送信してみます。

バリデーションエラー
バリデーションエラー時の表示
バリデーションエラーの場合は、エラーメッセージが表示されています。
バリデーション通過
バリデーション通過時の表示
バリデーションエラーの場合は、エラーメッセージが表示されています。

これで一通りのバリデーション機能を実装できました。

エラーメッセージを日本語化する

バリデーション実装は出来ましたが、エラーメッセージが英語なのでいまいち伝わらない…。ここはひとまず日本語化しておきたいところです。

Laravelには「ローカリゼーション機能」というものがあり、バリデーションや認証、ページネーション時の表示文字列(ラベル・メッセージ)を言語別に持っておき、設定言語に合わせて表示できるナイスなものがあります。

これを日本語に対応させ、エラーメッセージを日本語化しましょう。

ロケール設定

まずは、現在の言語設定を確認します。

laravel/config/app.phpを開いて、以下2つのlocale設定を確認します。

/*
|--------------------------------------------------------------------------
| 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' => 'en',

/*
|--------------------------------------------------------------------------
| Application Fallback Locale
|--------------------------------------------------------------------------
|
| The fallback locale determines the locale to use when the current one
| is not available. You may change the value to correspond to any of
| the language folders that are provided through your application.
|
*/

'fallback_locale' => 'en',
アプリケーションのロケール設定
'locale' => 'en',
アプリケーションロケールによって、翻訳サービスプロバイダによって使用されるデフォルトのロケールが決定されます。 この値は、アプリケーションがサポートするロケールのいずれかに自由に設定できます。
アプリケーションフォールバックロケール
'fallback_locale' => 'en',
フォールバックロケールは、現在のロケールが使用できない場合に使用するロケールを決定します。 アプリケーションによって提供される任意の言語フォルダに対応するように値を変更することができます。

という事で、結論を言えば以下のように変更します。

'locale' => 'ja',
'fallback_locale' => 'en',

ロケール設定を日本語にし、フォールバックロケールを英語にする事で、日本語でのメッセージの用意がないものに関しては英語のメッセージが表示される。という事になります。

日本語ファイルの設定

まずは言語ファイルの確認を行います。

言語ファイルはlaravel/resources/lang配下にあります。

laravel
├ resources
│ ├─ lang
│ │   └─ en
│ │   ├─ auth.php
│ │   ├─ pagination.php
│ │   ├─ passwords.php
│ │   └─ validation.php

見てなんとなくお気づきかと思いますが、Laravelではデフォルトは英語の言語ファイルしかありません。なので、日本語ファイルを作成する必要があります。

手順としては以下の通りです。

  1. laravel/resources/lang/en ディレクトリを丸ごとコピーする
  2. コピーしたディレクトリ名を ja へ変更する。
  3. jaディレクトリ内のvalidation.phpに記述されている英文を日本語に置き換える

最終的にディレクトリ構成は以下のようになります。

laravel
resources
│ ├─ lang
│ │   ├─ en
│ │   │ ├─ auth.php
│ │   │ ├─ pagination.php
│ │   │ ├─ passwords.php
│ │   │ └─ validation.php
│ │  
│ │   └─ ja
│ │ ├─ auth.php
│ │ ├─ pagination.php
│ │ ├─ passwords.php
│ │ └─ validation.php

ここでネックになるのが手順3の日本語への置き換えですが、一度に全てはなかなか難しいと思うので(それなりに数があります)、自分で行う場合はひとまずは使うエラーメッセージだけ日本語に置き換えるという流れで良いと思います。

今回は日本語への翻訳をこちらで行ったものを以下に記します。

laravel/resources/lang/ja/validation.php
<?php

return [
'accepted' => ':attribute が未承認です',
'active_url' => ':attribute は有効なURLではありません',
'after' => ':attribute :date より後の日付にしてください',
'after_or_equal' => ':attribute :date 以降の日付にしてください',
'alpha' => ':attribute は英字のみ有効です',
'alpha_dash' => ':attribute は「英字」「数字」「-(ダッシュ)」「_(下線)」のみ有効です',
'alpha_num' => ':attribute は「英字」「数字」のみ有効です',
'array' => ':attribute は配列タイプのみ有効です',
'before' => ':attribute :date より前の日付にしてください',
'before_or_equal' => ':attribute :date 以前の日付にしてください',
'between' => [
'numeric' => ':attribute :min :max までの数値まで有効です',
'file' => ':attribute :min :max キロバイトまで有効です',
'string' => ':attribute :min :max 文字まで有効です',
'array' => ':attribute :min :max 個まで有効です',
],
'boolean' => ':attribute の値は true もしくは false のみ有効です',
'confirmed' => ':attribute を確認用と一致させてください',
'date' => ':attribute を有効な日付形式にしてください',
'date_format' => ':attribute :format 書式と一致させてください',
'different' => ':attribute :other と違うものにしてください',
'digits' => ':attribute :digits 桁のみ有効です',
'digits_between' => ':attribute :min :max 桁のみ有効です',
'dimensions' => ':attribute ルールに合致する画像サイズのみ有効です',
'distinct' => ':attribute に重複している値があります',
'email' => ':attribute メールアドレスの書式のみ有効です',
'exists' => ':attribute 無効な値です',
'file' => ':attribute アップロード出来ないファイルです',
'filled' => ':attribute 値を入力してください',
'gt' => [
'numeric' => ':attribute :value より大きい必要があります。',
'file' => ':attribute :value キロバイトより大きい必要があります。',
'string' => ':attribute :value 文字より多い必要があります。',
'array' => ':attribute には :value 個より多くの項目が必要です。',
],
'gte' => [
'numeric' => ':attribute :value 以上である必要があります。',
'file' => ':attribute :value キロバイト以上である必要があります。',
'string' => ':attribute :value 文字以上である必要があります。',
'array' => ':attribute には value 個以上の項目が必要です。',
],
'image' => ':attribute 画像は「jpg」「png」「bmp」「gif」「svg」のみ有効です',
'in' => ':attribute 無効な値です',
'in_array' => ':attribute :other と一致する必要があります',
'integer' => ':attribute は整数のみ有効です',
'ip' => ':attribute IPアドレスの書式のみ有効です',
'ipv4' => ':attribute IPアドレス(IPv4)の書式のみ有効です',
'ipv6' => ':attribute IPアドレス(IPv6)の書式のみ有効です',
'json' => ':attribute 正しいJSON文字列のみ有効です',
'lt' => [
'numeric' => ':attribute :value 未満である必要があります。',
'file' => ':attribute :value キロバイト未満である必要があります。',
'string' => ':attribute :value 文字未満である必要があります。',
'array' => ':attribute :value 未満の項目を持つ必要があります。',
],
'lte' => [
'numeric' => ':attribute :value 以下である必要があります。',
'file' => ':attribute :value キロバイト以下である必要があります。',
'string' => ':attribute :value 文字以下である必要があります。',
'array' => ':attribute :value 以上の項目を持つ必要があります。',
],
'max' => [
'numeric' => ':attribute :max 以下のみ有効です',
'file' => ':attribute :max KB以下のファイルのみ有効です',
'string' => ':attribute :max 文字以下のみ有効です',
'array' => ':attribute :max 個以下のみ有効です',
],
'mimes' => ':attribute :values タイプのみ有効です',
'mimetypes' => ':attribute :values タイプのみ有効です',
'min' => [
'numeric' => ':attribute :min 以上のみ有効です',
'file' => ':attribute :min KB以上のファイルのみ有効です',
'string' => ':attribute :min 文字以上のみ有効です',
'array' => ':attribute :min 個以上のみ有効です',
],
'not_in' => ':attribute 無効な値です',
'not_regex' => 'The :attribute format is invalid.',
'numeric' => ':attribute は数字のみ有効です',
'present' => ':attribute が存在しません',
'regex' => ':attribute 無効な値です',
'required' => ':attribute は必須です',
'required_if' => ':attribute :other :value には必須です',
'required_unless' => ':attribute :other :values でなければ必須です',
'required_with' => ':attribute :values が入力されている場合は必須です',
'required_with_all' => ':attribute :values が入力されている場合は必須です',
'required_without' => ':attribute :values が入力されていない場合は必須です',
'required_without_all' => ':attribute :values が入力されていない場合は必須です',
'same' => ':attribute :other と同じ場合のみ有効です',
'size' => [
'numeric' => ':attribute :size のみ有効です',
'file' => ':attribute :size KBのみ有効です',
'string' => ':attribute :size 文字のみ有効です',
'array' => ':attribute :size 個のみ有効です',
],
'string' => ':attribute は文字列のみ有効です',
'timezone' => ':attribute 正しいタイムゾーンのみ有効です',
'unique' => ':attribute は既に存在します',
'uploaded' => ':attribute アップロードに失敗しました',
'url' => ':attribute は正しいURL書式のみ有効です',

'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],

'attributes' => [],

];

バリデーションも含め、翻訳した日本語言語ファイルセットはGitHubから取得できます。

[GitHub]Laravel 日本語言語ファイル

最新版はver 5.7用ですが、ver 5.6や5.5用のものもブランチを切り替える事で取得可能です。

プレースホルダー(置換)部分は敢えてそのまま残してあるので、自分の使いやすいように適宜変更して使ってください。

日本語ファイルの上書きが完了したら、再度アクセスしてバリデーションエラーを発生させてみます。

バリデーションエラー時の日本語表示

きちんとエラーメッセージが日本語になっていますね。これで基本型でのバリデーション実装は一通り完了です。

まとめ

今回の作業は以上です。Laravelのバリデーションは簡単に書けてルールも豊富。とても使い勝手が良いので是非試してみてください。

ちなみに今回はバリデーション入門編という事で基本型の実装を行いました。次回は一歩踏み込んで、バリデーションロジックをコントローラから分離させる流れを組んでみたいと思います。

next:Laravelのフォームリクエストクラスでバリデーションロジックをコントローラから分離する