Laravelアプリケーション学習⑩（バリデーション機能）

本記事ではLaravelのアプリケーションを理解し、より良い設計・アーキテクチャを構築できるように学習したことを簡潔にまとめています。

#目次
1.Laravelのアーキテクチャ
2.アプリケーションのアーキテクチャ
3.<font color="LimeGreen">HTTPリクエストとレスポンス</font>
4.データベース
5.認証と許可
6.イベントとキューによる処理の分離
7.コンソールアプリケーション
8.テスト
9.エラーハンドリングとログの活用
10.テスト駆動開発の実践

#3．HTTPリクエストとレスポンス

#3.2 バリデーション
Laravelには、バリデーションを簡単に実装できる機能が用意されている。下記に簡単な利用例を示す。

```php
<?php
declare(strict_types=1);

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Vaildator;

class UserController extends Controller
{
   public function register(Request $request) {
   {
      //すべての入力値を取得し、$inputsに保持する
      $inputs = $request->all();
      
      // バリデーションルールを定義する
      $rules = [
         'name' => 'required',
         'age' => 'integer',
      ];
   
      // バリデータクラスのインスタンスを取得
      $validator = Validator::make($inputs, $rules); // ①
　　　
　　　if ($validator->fails()) {
         // 値エラーの場合の処理
      }
   }
}
```
上記コード例では、ユーザーから入力された項目の配列$inputsとバリデーションルールを定義した配列$rulesを、Validatorファサードのmakeメソッドに渡し、$validatorインスタンスを取得する。これは、Illuminate\Validation\Validatorクラスのインスタンスであり、failsメソッドが実行されるタイミングでバリデーションが行われる。


##　バリデーションルールの指定方法
下記に示す通り、検査対象とする入力項目のキー名（inputのname属性)と、ルールを表す文字列の組み合わせを連想配列で指定する。指定方法は[]と|の２種類ある。

```php
[
   'email' => ['required', 'email'], // 推奨
   'age' => 'required|integer',
]
```

## バリデーションルール
バリデーションのルールには豊富な種類が用意されている。以降の記事ではルールの種類を５つのタイプに分類して解説する。

1.値の存在確認を行うルール
2.型やフォーマット確認を行うルール
3.桁数や文字数、サイズ確認を行うルール
4.他の対象との比較を行うルール
5.バリデーション処理に対するルール

### 1.値の存在を確認するルール
フィールドに値が指定されているかの確認を行うルールで、「required」などが該当。

```
$rules = [
   'name' => 'required',
];
```
上記のコード例の***「required」***ルールは、nameフィールドに値が指定されているかの確認、***「present」***はフィールドが存在するかの確認、***「filled」***はフィールドが存在する場合のみ必須チェックを行う。

### 2.型やフォーマットを確認するルール
数値であるか、文字列であるかなど、型やフォーマットの確認を行う。

```php
$rules = [
   'user_no' => 'numeric', //数値であるかの確認
   'alpha' => 'alpha', // すべて英数であることの確認
   'email' => 'email', 　// メールアドレス形式であることの確認
   'ip_address' => 'ip',　// IPアドレスであることの確認
];
```

### 3.　桁数や文字数、サイズを確認するルール
桁数や文字数のチェックも可能です。桁数や文字数は「ルール：パラメータ」の形式で指定。

桁数や文字数

```php
$rules = [
   'bank_pass' => 'digist:4', // 4桁の数値
   'signal_color' => 'in:green, red, yellow', // 3種類のうちいずれかであることの確認
];
```
下記にsizeを用いて指定した方法を示す。

```php
$rules = [
   'number' => ['integer',  'size:10'], //10であることの確認
   'name' => size:10, //10文字であることの確認
   'button' => ['array', 'size:10'], //要素数10であることの確認
   'upload' => ['file', 'size:10'], // ファイルサイズ10KBであることの確認
]
```
sizeと同様に「between」や「max」、「min」などで値の範囲を指定可能である。
また、***「regex」***を使うと正規表現でルールを指定できる。

```php
$rules = [
   'user_id' => 'regex:/^[0-9a-zA-Z]+$/', // 半角英数字
];
```

### 4.他の対象と比較するルール
入力値だけでなく、フィールドとの比較も可能である。***「」***ルールは、対象フィールドの値と「フィールド名_confirmation」をフィールド名に持つ値が同値であることを確認する。

```php
$rules = [
   //emailとemail_confirmationが同値であることの確認
   'email' => 'confirmed',
];
```
***「unique」***ルールはデータベースと組み合わせて使用するルールである。
引数の数によって挙動が異なるので注意が必要。

```php
$rules = [
   // ①usersテーブルの同名カラム(name)と比較して、重複しないことの確認
   'name' => 'unique:users', 
   
   // ②usersテーブルのemailカラムと,mailフィールドの値を比較して重複していないかの確認
   'mail' => 'unique:users, email',

   // ③ 上記の②と同じルールだが、idが100のレコードは重複を許可
   'mail' => 'unique:users, email, 100',
];
```

### 5. バリデーション処理に対するルール
***「bail」***ルールを使うと、バリデーションエラーになった場合に、以降のバリデーションを行わない指定が可能である。

```php
$rules = [
   'email' => ['bail', 'required', 'unique:posts', 'email'],
   'name' => ['required', 'max:255'],
];
```
上記コード例では、emailフィールドでエラーとなった場合、nameフィールドのバリデーションは実行されない。


##　バリデーション機能の利用
バリデーション機能の利用方法として、下記の２つの方法を紹介する。

１．コントローラのバリデーション
２．フォームリクエストのバリデーション

### 1.　コントローラのバリデーション
Laravelのコントローラにはルールを用意するだけでバリデーションを行える***validate***メソッドが用意されている。

```php
<?php
declare(strict_types=1);

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
   public function register(Request $request)
   {
      $rules = [
         'name' => ['required', 'max:20'],
         'email' => ['required', 'email'],
      ];
   
      // ①バリデーションの実行（エラーの場合は直前の画面にリダイレクト）
      $this->validate($request, $rules);

      $neme = $request->get('name');
   }
}
```
バリデーションを通過できない入力が１つでもあると、validateメソッドでエラーメッセージと入力をセッションに保存し、直前のHTTPメソッド/URIへリダイレクト処理を行う。

上記のvalidateメソッドは便利だが、直前の画面ではなく専用のエラー画面への繊維や、独自の処理を実行したい場合、Validatorクラスのインスタンスを生成し、failsメソッドを呼び出す。

下記にその例を示す。

```php
<?php
declare(strict_types=1);

namespace App\Http\Controllers;

use Iluminate\Http\Request;
use Illuminate\Support\Facades\Validator;

class UserController extends Controller
{
   public function register(Request $request)
   {
      $rules = [
         'name' => ['required', 'max:20'],
         'email' => ['required', 'email'],
      ];

      $inputs = $request->all();

      // バリデータクラスのインスタンスを生成
       $validator = Validator::make($inputs, $rules);

      if ($validator->fails()) {
         // ここでバリデーションエラーの場合に専用のエラー画面などに遷移させる
      }

      $neme = $request->get('name');
      --[略]--
   }
}
```

### 2. フォームリクエストによるバリデーション
前の記事でも説明したので割愛する。

フォームリクエストでバリデーションルールを設定した処理でバリデーションエラーが発生した場合、フォームリクエスト内でリダイレクトされコントローラには処理が渡らない。
フォームリクエスト内でリダイレクト処理が行われ、呼び出し元の画面に遷移する。
なお、エラー発生時の処理はフォームリクエストのメソッドオーバーライドやプロパティ設定でカスタマイズ可能である。（Illuminate\Foundation\Http\FormRequestを参照）

## バリデーション失敗時の処理
Laravelでは、バリデーションチェックの結果は***「MessageBag」オブジェクト***で保持されている。Illuminte\Support\MessageBagクラスのインスタンスである。
ビューでは常に\$errosの名前でMessageBagインスタンスが用意されているため、保持しているセッション中に「error」キーがあれば、それらが利用できる。$errorsは常に存在しているため、allやget、firs、hasメソッドなどが利用できる。

```php
<html>
<head>
   <meta charset='utf-8'>
</head>
<body>
<ul>
@if (count($errors) > 0)
   @foreach ($errors->all() as $error)
      <li> {{ $error }}</li>
   @endforeach
@endif
</ul>
```

また、英語表記のエラーメッセージをカスタマイズして、任意の文字列に変更する方法を下記コード例に示す。

```php
フォームリクエストにメッセージ指定
declare(strict_types=1);

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class UserRegistPost extends FormRequest
{
   public function message()
   {
      return [
         'name.required' => '名前は必ず入力してください',
         'name.max' => '名前は最大20文字まで入力できます。',
         'email.required' => 'メールアドレスは必ず入力してください',
         --[略]--
      ];
   }
}
```

##　バリデーションのカスタマイズ
実際のアプリケーション開発では、独自のルールの定義が必要になる場合がる。
下記にルールをカスタマイズ方法を３つ紹介する。

1.　バリデータクラスを実装する　（サイト共通で使いたい場合）
2.　Rule（ルール）オブジェクトの利用（頻繁には使わないが、ある程度利用する）
3.　extendメソッドを使用する　（１回しか利用しない）
4.　sometimesメソッドで特定の条件のみ追加する　（条件でルールを追加する際に利用）

## 1.オリジナルバリデータの作成
始めに、Illuminate\Validation\Validatorクラスを継承したクラスを新しく作成する。
今回の例では、Http\Validators\HelloValidator.phpを作成

```php
use Illuminate\validation\Validator;

class HelloValidator extends Validator
{
   public function validateHello($attribute, $value, $parameters)
   {
      // バリデーションの処理（ひらがなならtrue）
      return (preg_match('/[^ぁ-んー]/u', $value) !== 0);
   }    
}
```

次にサービスプロバイダでHelloValidatorを組み込む。HelloServiceProvider.phpを作成後、boot()を修正

```php
// use Validator;
// use App\Http\Validators\HelloValidator;

public function boot() {
   $validator = $this->app['Validator'];
   $validator->resolver(function ($translator, $data, $rules, $message) {
      return new HelloValidator($translator, $data, $rules, $messages);
   });
}
```

フォームリクエストを作成し、rules()にhelloを追加する。

```php
class HelloRequest extends FormRequest
{
    public function rules()
    {
        return [
          'name'=>['required', 'hello'],
          'mail'=>'email',
        ];
    }

   public function messages()
   {
      return [
         'name.required' => '名前は必須です',
         'name.hello' => 'Hello入力は偶数のみです',
}
```

## 2.　Ruleオブジェクトの利用
App\Rulesは以下にルールのクラスを作成。

```
php artisan make:rule Phone
```

実行するとPhone.phpファイルが作成される。

`passes()`に、バリデーション判定のルールを返すように記載し、
passes()の引数`$attribute`にはフィールド名、`$value`にはそのフィールどの値が入る。

下記にPhone.phpを示す。

```php
<?php
namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class Phone implements Rule
{
   public function __construct()
   {
   }

   public function passes($attribute, $value)
   {
      return preg_match('/^[0-9]{2,4}-[0-9]{2,4}-[0-9]{3,4}$/', $value);
   }

   public function message()
   {
      return '正しい電話番号を入力してください';
   }
```
作成した独自ルールを使用するには、use文で呼び出し、newでインスタンスを生成する。

```php
<?php

use App\Rules\Phone;

class PhoneController extends Controller
{
   public function store(Request $request)
   {
      $request->validate([
         'user_name' => 'required',
         'phone_num' => ['required', new Phone()],
      ]);
   }
}
```

## 3. extendによるルールの追加
Validatorクラスの`extendメソッド`を紹介する。extendメソッドは引数にクロージャを指定し、クロージャ内でルールを定義する。
下記にextendによるルール追加の具体例を示す。

```php
<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

class UserController extends Controller
{
   public function register(Request $request)
   {
      // [name]のバリデーションルールに[ascii_alpha]を追加
      $rules = [
         'name' => ['required', 'max:20', 'ascii_alpha'],
         'email' => ['required', 'email', 'max:255'],
      ];
     
      $inputs = $request->all();

      // バリデーションルールに「ascii_alpha」を追加
      Validator::extend('ascii_alpha', function($attribute, $value, $parameters) {
         // 半角アルファベットならtrueとする。
         return preg_match('/^[a-zA-Z]+$/', $value);
      ]);

      $validator = Validator::make($inputs, $value);

      if ($validator->fails()) {
         // ここにバリデーションクラスの場合の処理
      }

      $name = $request->input('name');
   }
}
```

## 4.条件によるルールの追加
特定の条件のみバリデーションを追加するには、バリデータクラスの`sometimesメソッド`を使用する。

```php
$rules = [
   'name' => ['required', 'max:20'],
   'email' => ['required', 'email', 'max:255'],
];

$inputs = $request->all();

$validator = Validator::make($inputs, $rules);
$validator->sometimes('age', 'integer|min:18', function ($inputs) {
   return $intpus->mailmagazine === 'allow';
});

`sometimesメソッド`は、第三引数のクロージャがtrueの場合に、第一引数の入力項目に対して、第２引数のルールを適用する。
```