Posted at

LravelでValidationを実装した話

More than 1 year has passed since last update.


概要


  • バリデーションエラーログのフォーマットをカスタマイズ


バージョンとか


  • Laravel 5.6

  • PHP7.2


シナリオ

Webアプリケーションを作成する案件で、フレームワークや言語等は問わない。

今回は、 PHPの Laravel で開発することにしました。

社内では GolangNodeJs で行うのがデフォルトっぽく、

特に Golang をメインで開発を行っているみたいです。


管理方法

管理管轄

|
+- DockerContainer(App) Golangだったり
|
+- DockerContainer(App) Nodejsだったり
|
+- DockerContainer(本App)
|
+- src
|
[Laravel Project]

Dockerでまとめて管理する方法で、エラーログをAPPごとに標準出力で出すことにより、

管理側で一括でログを確認できるような構造になっています。


フォーム内容

form-data 形式でフォームデータをPOSTでリクエストします。

name        = (必須,英数字+[.-_],255文字以内)

id = (必須,英数字20~24桁)
recorded_at = (必須,タイムスタンプ数値10桁)


バリデーション

バリデーションのフォーマットは仕様書により固定されており、

以下がバリデーションエラー時のフォーマットである。

Key: {keyname}, Value: {values}, Violation: {violation}


API

バリデーション時のAPIレスポンスは基本400エラーとします

エラーレスポンスはJSONで返します。

バリデーションエラーではないものは、メッセージをそのまま返します。

(以下の表はすべてサンプルで作成した仕様です)

エラーコード
フォーマット
内容

A0001
Key:, Value:, Violation:
バリデーションエラー時

A0005
File Extension Error
ファイル拡張子エラー時

{

"errors":[
{
"code":"A0001",
"message":"Key: {keyname}, Value: {values}, Violation: {violation}"
},
{
"code":"A0005",
"message":"File Extension Error"
},
]
}


本題

Laravelでバリデーションエラーが出た場合、自動的にメッセージが作成されますが、

今回は指定されたメッセージに変更しないといけないので、変更していきます。


基本

RouteやControllerなどの説明は省きます。

基本的なバリデーションはこちらのコードで行なえます

$validator = Validator::make($request->all(), [

"name" => "required|max:255|not_regex:/[^a-zA-Z0-9._-]/",
"id" => "required|not_regex:/[^a-zA-Z0-9]/",
"recorded_at" => "required|digits:10"
]);

エラーハンドリングをする際は、以下のコードでハンドリングが行えます。

if ($validator->fails()) {

foreach ($validator->errors()->all() as $error_text) {
// $error_textにエラー内容が入る
}
}


カスタムエラーメッセージ

カスタムエラーメッセージを使用したい場合は、Validator::make()の第三引数に配列で指定できます。

指定の方法はたくさんありますので公式を御覧ください。

https://laravel.com/docs/5.6/validation#custom-error-messages


$err_msg = "Key: :attribute, Value: :values, Violation: ";
$custom_error_msg = [
"required" => $err_msg . "required",
"max" => $err_msg . "maximum length",
"alpha_num" => $err_msg . "alphabet&number",
"digits" => $err_msg . "number length",
"device_id.not_regex" => $err_msg . "alphabet&number",
"name.not_regex" => $err_msg . "alphabet&number+[._-]"
];

$validator = Validator::make($request->all(), [
"name" => "required|max:255|not_regex:/[^a-zA-Z0-9._-]/",
"device_id" => "required|not_regex:/[^a-zA-Z0-9]/",
"recorded_at" => "required|digits:10"
], $custom_error_msg);

ですが、これだとエラー出力時にこう出力されます。

{

"errors":[
{
"code":"A0001",
"message":"Key: name, Value: :values, Violation: alphabet&number+[._-]"
},

:valuesが置換されていません。

LaravelのValidatorは標準で :attribute をKey名に置換されて出力されるのですが、

それ以外のプレースホルダーは自分で実装しなければいけません。


カスタムプレースホルダー

まず Illuminate\Validation\Validator を継承したサービスモジュールを作成します。


app/Service/TestValidator.php


<?php
namespace App\Service;
use Illuminate\Validation\Validator;

class TestValidator extends Validator
{
public function __construct($translator, $data, $rules, $messages = [])
{
parent::__construct($translator, $data, $rules, $messages);

// ここに記述
}
}


次に、Replacerを実装します。


app/Service/TestValidator.php


<?php
namespace App\Service;

use Illuminate\Validation\Validator;

class TestValidator extends Validator
{
public function __construct($translator, $data, $rules, $messages = [])
{
parent::__construct($translator, $data, $rules, $messages);

// $message = カスタムエラーメッセージの内容
// $attribute = エラーが発生したキー名
// $rule = バリデーションのルール 例:required
// $parameters = 場合によって変わるが、digitsであれば文字数などが配列で入る
//
// クロージャなので、$dataを値渡ししてあげる。
// $data = リクエストデータが入る
$replacer = function($message, $attribute, $rule, $parameters) use ($data) {

// $message 内の :values を $data[$attribute] に置換する
return str_replace(':values', $data[$attribute], $message);
};

// 上記のクロージャを適応するルール
$this->addReplacers([
"required"=>$replacer,
"max" => $replacer,
"alpha_num" => $replacer,
"digits" => $replacer,
"mimes" => $replacer,
"file" => $replacer,
"not_regex" => $replacer
]);

}
}


$this->addReplacers() により、置換が行われます。


ValidatorServiceProviderを作成

このままではValidator::makeを実行しても上記の TestValidator を読み取ってくれないので、

ValidatorServiceProviderを作成する。


app/Providers/ValidatorServiceProvider.php


<?php

namespace App\Providers;

use App\Service\TestValidation;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;

class ValidatorServiceProvider extends ServiceProvider
{
/**
* Bootstrap services.
*
* @return void
*/

public function boot()
{
Validator::resolver(function ($translator, $data, $rules, $messages) {
return new TestValidation($translator, $data, $rules, $messages);
});
}

/**
* Register services.
*
* @return void
*/

public function register()
{
//
}
}


これでしっかりエラーメッセージが表示されます。

{

"errors":[
{
"code":"A0001",
"message":"Key: name, Value: 全角文字ダメ!, Violation: alphabet&number+[._-]"
},


おわりに

やり方がわかるまで苦労しましたが、そこまで変更点もなく自由にエラーメッセージを変更できたのでとても楽でした。

ずっとLaravel使ってるので、エラーハンドリングとかをもっとマスターしていきたいなと。

以上