Chapter4　バリデーションをカスタマイズする（4-3）

# バリデーションをカスタマイズする

## フォームリクエスト

コントローラはそれぞれのアクションで実行すべきビジネスロジックなどを実行することになるため、
「入力された値のチェック」などはできれば他に切り離したい。
（フォームの項目にバリデーション機能を実装する際に、各コントローラに書くのは面倒。）

そこでLarabelでは「フォームリクエスト」と呼ばれる機能を考えました。

## フォームリクエストの作成

フォームリクエストは「artisan make:request」というコマンドを使って作成します。

```
% php artisan make:request HelloRequest
```


### フォームリクエストの基本コード

```HelloRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class HelloRequest extends FormRequest
{
    
    public function authorize()
    {
        return false;
    }
    
    public function rules()
    {
        return [
            //
        ];
    }
}

```

- authorize
　フォームリクエストの利用が許可されているかどうかを指します。
　（trueだと許可され、falseだと不許可になりHttpExceptionの例外処理が発生してドーム処理が行えなくなる。）
- rules
　適用されるバリデーションの検証ルールを設定します。
　ここでreturnされた検証ルールをもとに、FormRequestでバリデーションチェックが実行されます。

### フォームリクエストの修正

```HelloRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class HelloRequest extends FormRequest
{

    **「Hello」からのアクセスの時のみフォームリクエストを使用できるようにする**
    public function authorize()
    {
        if ($this->path() == 'hello')
        {
          return true;
        } else {
          return false;
        }

    }
　　　　　　　　**バリデーション検証ルールを設定する**
    public function rules()
    {
        return [
          'name' => 'required',
          'mail' => 'email',
          'age' => 'numeric|between:0,150',
        ];
    }
}
```


### メッセージのカスタマイズ

エラーメッセージをカスタマイズするには、
フォームリクエストに「messages」というメソッドを追加します。

```HelloRequest.php
public function messages()
    {
        return [
            'name.required' => '名前は必ず入力してください。',
            'mail.email' => 'メールアドレスが必要です。',
            'age.numeric' => '年齢を整数で記入ください。',
            'age.between' => '年齢は0〜150の間で入力してください。',
        ];
    }
```

この`messages`は、FormRequestのバリデーション機能が
エラーメッセージを必要とした時に呼び出されるメソッドです。

```sample.php
return [
            '**項目名**.**ルール名**' => '**メッセージ**',
        ];
```

1つの項目に複数のルールが設定してある場合は、ルールごとに設定する。
（設定していない場合は、デフォルトのメッセージ＜英語＞がそのまま使われます。）


## バリデータを使う

「エラーがあったらフォームページにリダイレクトせず、別の処理を行わせたい」時や、
「フォームの値以外でバリデーションチェックを行わせたい」時に、
**バリデータ**を独自に用意して処理することもできます。

```HelloController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\Response;

use App\Http\Requests\HelloRequest;
use Illuminate\Support\Facades\Validator;　　**バリデータ使えるよう追記**

class HelloController extends Controller
{
    public function index(Request $request) {

        return view('hello.index',['msg'=>'フォームを入力']);

    }

    public function post(Request $request) {

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

            'name' => 'required',
            'mail' => 'email',
            'age' => 'numeric|between:0,150',

        ]);
        if ($validator->fails()) {
            return redirect('hello')
                ->withErrors($validator)
                ->withInput();
        };

        return view('hello.index',['msg'=>'正しく入力されました！']);

    }

}
```
バリデータを使用できようにする際の追記の仕方がうまくいかなかったので、
以下のURLを参考に`use Illuminate\Support\Facades\Validator;`と
追加するすることで利用できるようになった。

[call to undefined method Dotenv\Validator::make](http://qiita.com "https://laracasts.com/discuss/channels/laravel/call-to-undefined-method-dotenvvalidatormake")

### バリデータの基本

```sample.php
validator = Validator::make(**値の配列**,**ルールの配列**)
```

第一引数に、チェックする値をまとめた配列を用意する。
第二引数に、バリデータで使用する検証ルールの情報を配列にまとめたものを指定する。

```sample.php
if ($validator->fails()) {**エラー時の処理**};
```
「fail」はValidatorクラスにあるメソッドで、バリデーションチェックに失敗したかどうか確認するためのメソッド。
戻り値が「true」ならエラーが発生しているので、
エラー時の処理を記述します。

```sample.php
if ($validator->fails()) {
    return redirect('hello')
        ->withErrors($validator)
        ->withInput();
 };
```
エラーが発生したらGETページにリダイレクトする処理を記述。
単にリダイレクトするだけでは、エラーメッセージやフォームの値などが受け取れないので、
上記のように記述します。

- withErrors
　　エラーメッセージの情報をリダイレクト時に渡す。
- withInput
　　フォームの入力情報をリダイレクト時に渡す。


## クエリー文字列にバリデータを適用する

```HelloControllor.php
public function index(Request $request) {

        $validator = Validator::make($request->query(), [
            'id' => 'required',
            'pass' => 'required',
        ]);
        if ($validator->fails()) {
            $msg = 'クエリ文字に問題がります。';
        } else {
            $msg = 'ID/PASSを受け付けました。フォームを入力ください。';
        };

        return view('hello.index',['msg'=>$msg,]);

    }
```

`request->query()`は、送信されたクエリー文字列を配列の形にまとめたものを返す。

### エラーメッセージのカスタマイズ

```sample.php
validator = Validator::make(**値の配列**,**ルールの配列**,**メッセージ配列**)
```

```HelloControllor.php
public function post(Request $request) {

        $rules = [

            'name' => 'required',
            'mail' => 'email',
            'age' => 'numeric|between:0,150',

        ];
        $messages = [
            'name.required' => '名前は必ず入力してください。',
            'mail.email' => 'メールアドレスが必要です。',
            'age.numeric' => '年齢を整数で記入ください。',
            'age.between' => '年齢は0〜150の間で入力してください。',
        ];
        $validator = Validator::make($request->all(), $rules,$messages);
        if ($validator->fails()) {
            return redirect('hello')
                ->withErrors($validator)
                ->withInput();
        };

        return view('hello.index',['msg'=>'正しく入力されました！']);

    }
```

### 条件に応じてルールを追加する

```sample.php
validator->sometimes(**項目名**,**ルール名**,**クロージャ**)
```
**クロージャ**は、ルールをを追加すべきどうかを決定するものです。

```sample.php
function($input){
   **処理を実行**
　　　　　return 真偽値;
}
```

引数`$input`は入力された値をまとめたものが渡される。
ここから、`$input->name`といった具合にしてフォームの値を取り出すことができます。
戻り値は、ルールを追加するべきかどうか指定する真偽値になります。

`false`の場合は、sometimesで指定したルールを指定した項目に追加します。

### ルール追加の記述を追加する

```HelloControllor.php
public function post(Request $request) {

        $rules = [

            'name' => 'required',
            'mail' => 'email',
            'age' => 'numeric',  **条件を整数のみに変更**

        ];
        $messages = [

            'name.required' => '名前は必ず入力してください。',
            'mail.email' => 'メールアドレスが必要です。',
            'age.numeric' => '年齢を整数で記入ください。',
            'age.min' => '年齢はゼロ歳以上で記入ください。',  **条件を追加**
            'age.max' => '年齢は200歳以下で記入ください。',　　　**条件を追加**

        ];
        $validator = Validator::make($request->all(), $rules,$messages);
　　　　　　　　　　　　　　　　**追加したい条件を定義する**
        $validator->sometimes('age', 'min:0',function ($input){
            return !is_int($input->age);
        });
　　　　　　　　　　　　　　　　**追加したい条件を定義する**
        $validator->sometimes('age', 'max:200',function ($input){
            return !is_int($input->age);
        });

        if ($validator->fails()) {
            return redirect('hello')
                ->withErrors($validator)
                ->withInput();
        };

        return view('hello.index',['msg'=>'正しく入力されました！']);

    }
```

```sample.php
$validator->sometimes('age', 'min:0',function ($input){
            return !is_int($input->age);
});
```

`!is_int($input->age);`により、`$input->age`の値が整数の場合は、
「false」が返され、`min:0`のルールが`age`に追加されます。


## オリジナルバリデータの作成

ルールそのものを新たに定義したい場合は、
「バリデータ」そのものを作成し、独自に処理を定義する方法があります。

バリデータクラスを作成する際に、コードを自動生成する機能はないので、
手作業でスクリプトファイルを作成する。


```HelloValidator.php
<?php
namespace App\Http\Validators;

use Illuminate\Validation\Validator;

class HelloValidator extends Validator
{

    public function validateHello($attribute, $value, $parameters)
    {
        return $value % 2 == 0;
    }
    
}
```


作成したHelloValidatorを組み込むためには、
サービスプロバイダを利用します。
以前作成した「HelloServiseProvider」というサービスプロバイダを再利用します。


```HelloServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\View;

use Dotenv\Validator;
use App\Http\Validators\HelloValidator;

class HelloServiceProvider extends ServiceProvider
{

    public function boot()
    {
        $validator = $this->app['validator'];

        $validator->resolver(function($translator, $data, $rules, $messages) {
            return new HelloValidator($translator, $data, $rules, $messages);
        });
    }

}
```

バリデータは、`$this->app['validator']`というところに保管されている。
この`resolver`というメソッドで、**リゾルブ**（バリデーションの処理を行う）の処理を設定できる。

```sample.php
function($translator, $data, $rules, $messages) {
    return **バリデータのインスタンス**;
}
```
この関数で、バリデータのインスタンスを戻り値で返すことによって、
このサービスプロバイダをバリデーションの処理として設定している。

`new HelloValidator`の引数には、`resolver`で渡された4つの引数を
そのまま指定している。

### HelloValidatorのルールを使用する

今回は以前に作成した「HelloRequest.php」を利用する。

```HelloRequest.php
class HelloRequest extends FormRequest
{

    
    public function rules()
    {
        return [
            'name' => 'required',
            'mail' => 'email',
            'age' => 'numeric|hello',    **バリデータを指定**
        ];
    }

    public function messages()
    {
        return [
            'name.required' => '名前は必ず入力してください。',
            'mail.email' => 'メールアドレスが必要です。',
            'age.numeric' => '年齢を整数で記入ください。',
            'age.hello' => 'Hello!入力は偶数のみ受け付けます。',　　**バリデータを使用した際のメッセージを変更**
        ];
    }

}
```


### `Validator::extend`を利用する

「このフォームでだけ、ちょっとしたカスタマイズしたルールを使いたい」場合は、
Validatorクラスの「extend」メソッドを利用します。

```sample.php
Validator::extend(*名前*, *クロージャ*);
```

第一引数は、指定した名前で、
第二引数は、クロージャをルールとして追加します。

クロージャは以下のように定義します。

```sample.php
public function validateHello($attribute, $value, $parameters,$validator)
{
     *バリデーションの処理*
     return *真偽値*;
}
```

引数にバリデータクラスを作成した時に定義したメソッドの引数`($attribute, $value, $parameters）`と
バリデータのインスタンスを付け加えた4つの値が用意されます。
戻り値に`true`を返せば、問題なし。
`false`を返せばエラーが発生したことを示します。

実行すると、「Call to undefined method Dotenv\Validator::extend()」と、
`Dotenv\Validator`クラスにある「extend」メソッドが定義されていないとエラーが出たので、
下記のURLを参考にuseを変更。

[Laravel入門】Validator::Extend](h[https://qiita.com/ne290205/items/e1e5532e920eafd67f96 "Qiita Home")

```HelloServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\View;

use Illuminate\Support\Facades\Validator;
use App\Http\Validators\HelloValidator;

class HelloServiceProvider extends ServiceProvider
{

    public function boot()
    {
        Validator::extend('hello', function($attribute, $value, $parameters, $validator) {
            return $value % 2 == 0;
        });
    }

}


```

## バリデーションルールを作る

Validatorクラスを利用したバリデータは、
サービスプロバイダのbootなどを組み込んで利用します。
この方法は、わかっていれば難しくはないが、バリデータの仕組みがわかっていないと、
うまく組み込めない可能性があります。

そこで、バリデータで利用する「ルール」を作って利用する方法もある。

バリデーションルールは「Illuminate\Contracts\Validation」名前空間の「Rule」という
クラスを継承して作られています。
このルールを継承したクラスを用意すれば、そのクラスをバリデーションのルールとして使えるようになります。

```
% php artisan make:rule Myrule
```

このコマンドによって、「app」のフォルダの中に、
「Myrule.php」というファイルが作られます。

### バリデーションルールの基本形

```Myrule.php
<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class Myrule implements Rule
{

    public function __construct()
    {
        //
    }

    public function passes($attribute, $value)
    {
        //
    }

    public function message()
    {
        return 'The validation error message.';
    }
}
```

- `passes` 
　`passes`は、ルールの通過条件を設定します、　
　引数にはルールの属性（ルールの後に用意される設定関係）をまとめた`$attribute`と、
チェックする値である`$value`が用意されています。

- `message`
 `message`は、問題発生時のメッセージを返すメソッドです。
　　ここでテキストを戻り値にすることで、エラーメッセージとして使うことができます。

### Myruleクラスを作成する

```Myrule.php
<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class Myrule implements Rule
{

    public $num=2;

    public function __construct($n)
    {
        $this->num = $n;
    }

    public function passes($attribute, $value)
    {
        return $value % $this->num == 0;
    }

    public function message()
    {
        return $this->num . 'で割り切れる値が必要です。';
    }

}
```

```HelloRequest.php
<?php

namespace App\Http\Requests;

use App\Rules\Myrule;
use Illuminate\Foundation\Http\FormRequest;

class HelloRequest extends FormRequest
{
    public function rules()
    {
        return [
            'name' => 'required',
            'mail' => 'email',
            'age' => 'numeric', new Myrule(5),
        ];
    }

}
```

> *エラーは出なかったが、正しく挙動しなかったので、復習時に再検証。