Laravel5.7のAPI開発でFormRequestを使ってバリデーションする

Laravel 5.7でAPI開発時にFormRequestを使ってバリデーションする方法をご紹介します。

実際に移植した時の話も含めて読みたい場合は[はてなブログに書いた記事](https://nakka-k.hatenablog.com/entry/2019/06/04/191906)をご覧ください。


(調べた感じLaravel5.5以降でないと違う書き方になるようです)


# FormRequestとは

FormRequestとはリクエストのバリデーションルールを定義するLaravelの仕組みです。
FormRequestをコントローラーメソッドにDIしてあげると、バリデーションが通った時だけコントローラー内の処理が走ります。

これによりコントローラーはリクエストに不正な値が入っている可能性を除外できるため、自分の処理だけに専念できます。


# FormRequestを作成する

まずArtisanCLIコマンドの`make:request`を使用して、FormRequestのファイルを作成しましょう。

```bash
php artisan make:request LoginRequest
```

`app/Http/Requests`以下にファイルが作成されます。
このファイルにバリデーションを移植します。

まず作成されたファイルはこのようになっています。

```php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class LoginRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return false;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            //
        ];
    }
}
```

ここで一番気をつけるべきなのは`public function authorize()`です。
デフォルトで`false`を返すようになっていますが、特にアクセス権限などを求めないリクエストであれば`true`を返すように書き換えてください。


## バリデーションルールを定義

まずバリデーションのルールを定義します。

```php
    public function rules()
    {
        return [
            'email'    => 'required|string|max:64',
            'password' => 'required|string|max:64',
        ];
    }
```

ルールはこれでOKです。

ですがこのままではバリデーションエラーが発生した時にHTMLを返してしまいますので、JSONを返すようにメソッドをオーバーライドしていきます。


## レスポンスを定義

`function failedValidation(Validator $validator)`を定義します。
ここではバリデーションを走らせて失敗した後のvalidatorが引数に渡されます。
ですから`$validator->errors()`などを駆使してレスポンスのjsonを生成しましょう。
そして戻り値には`HttpResponseException(response)`を返します。

```php
    protected function failedValidation(Validator $validator) {
        $res = response()->json([
            'errors' => $validator->errors(),
        ], 400);
        throw new HttpResponseException($res);
    }
```

## afterなどの追加処理を定義

`$validator->after()`などの特殊処理を定義する場合は`function withValidator(Validator $validator)`を定義します。
ここではバリデーションが実行される前に呼び出されるので、validatorを使って何か事前処理を書きたい場合に使います。

```php
    public function withValidator(Validator $validator) {
        $validator->after(function ($validator) {
            if ($this->input('name') === 'terminator') {
                $validator->errors()->add('name', 'ログインできません');
            }
        });
    }
```


# 完成したFormRequest

```php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;


class LoginRequest extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'email'    => 'required|string|max:64',
            'password' => 'required|string|max:64',
        ];
    }

    protected function failedValidation(Validator $validator) {
        $res = response()->json([
            'errors' => $validator->errors(),
        ], 400);
        throw new HttpResponseException($res);
    }

    public function withValidator(Validator $validator) {
        $validator->after(function ($validator) {
            if ($this->input('name') === 'terminator') {
                $validator->errors()->add('name', 'ログインできません');
            }
        });
    }
}
```


# FormRequestを組み込む

作成したLoginRequestをコントローラーに組み込んでバリデーションに使用してもらいましょう。
コントローラーメソッドの引数にあった`Request`を`LoginRequest`に変更するだけです。

```php
use App\Http\Requests\LoginRequest; // インポートを忘れずに


    public function login(LoginRequest $request) {
        // request is valid ......
    }
```



# まとめ

Controller内のバリデーションをFormRequestに抽出するだけ多くのメリットをもたらします。
何か事情がなければ最初からFormRequestを使った方がいいですね。


# 参考

- [【Laravel5】FormRequestのバリデーション結果をJSON APIで返す](https://qiita.com/junsan50/items/ec7f810decd3b82d3d76)
- [Laravel公式ドキュメント: Validation - Adding After Hooks To Form Requests](https://laravel.com/docs/5.8/validation#form-request-validation)