【Laravel5】FormRequestのバリデーション結果をJSON APIで返す

# 記事は個人サイトへ移動しました
https://www.luftgarden.jp/blog/1/

個人サイト／Qiitaどちらも同じ内容を記載しています（2020/2/24 現在）。

# 概要
例えば、会員を新規追加するAPIのエンドポイントがあるとします。

```text
[POST] https://example.com/api/users
```

```php:routes/api.php
Route::resource( 'users', 'Api\UsersController' )->only( ['store'] );
```

Controllerは以下の状態です。

```php:app/Http/Controllers/Api/UsersController.php
<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Http\Requests\StoreUserRequest;
use App\Http\Resources\UserResource;
use App\User;

class UsersController extends Controller
{
    /**
     * 会員追加
     *
     * @param StoreUserRequest $request
     * @return User
     */
    public function store( StoreUserRequest $request )
    {
        return User::create( $request->only( ['name'] ) ); 
    }
}

```

FormRequestクラスは以下の状態です。

```bash:artisan
$ php artisan make:request StoreUserRequest
```

```php:app/Http/Requests/StoreUserRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

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

    public function rules()
    {
        return [
            'name' => 'required',
        ];
    }

    public function messages()
    {
        return [
            'name.required' => 'お名前を入力してください。',
        ];
    }
}
```

上記APIを叩いた際に「お名前を入力してください」と
JSON形式でエラーメッセージを返却したい場合を考えます。

このままの状態でAPIを叩いても素直にエラーレスポンスが返りません。
（welcomeページのHTMLが返ってきたりする）

# 原因と対応
バリデーション失敗時にFormRequestクラス内でリダイレクトが発生することが原因のようです。
参考: [Laravel5.1.xでAPIを作る際に気になっていたことを調べました - Qiita](https://qiita.com/zaburo/items/f0db54bd3ebd81a8ce68)

バリデーション失敗時にもJSON形式で結果を返すように対応していきます。

## Laravel5.4以前
`FormRequest::response()` をオーバーライドすることで対応可能なようです。

```php:StoreUserRequest.php
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\JsonResponse;  // 追加

class StoreUserRequest extends FormRequest
{
    /**
     * [override] バリデーション失敗時ハンドリング
     *
     * @param array $errors
     * @return JsonResponse
     */
    public function response( array $errors )
    {
        $response = [
            'data'    => [],
            'status'  => 'NG',
            'summary' => 'Failed validation.',
            'errors'  => $errors,
        ];
        return new JsonResponse( $response, 422 );
    }
}
```

## Laravel5.5以降
`FormRequest::response()` は廃止されているため、代わりに
`FormRequest::failedValidation()` をオーバーライドすることで対応します。

```php:StoreUserRequest.php
<?php

namespace App\Http\Requests;

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

class StoreUserRequest extends FormRequest
{
    /**
     * [override] バリデーション失敗時ハンドリング
     *
     * @param Validator $validator
     * @throw HttpResponseException
     * @see FormRequest::failedValidation()
     */
    protected function failedValidation( Validator $validator )
    {
        $response['data']    = [];
        $response['status']  = 'NG';
        $response['summary'] = 'Failed validation.';
        $response['errors']  = $validator->errors()->toArray();

        throw new HttpResponseException(
            response()->json( $response, 422 )
        );
    }
}
```

# 実行結果
画像は [POSTMAN](https://www.getpostman.com/) のキャプチャです。
エンドポイントを叩いてJSONレスポンスが返却されればOKです。

![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3138343531382f35346430636161392d666263372d613837662d663966642d3231386131626435313536322e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184518/911e50ae-f3ab-b133-43cf-0413320f1b4c.png)


# API用のリクエストクラスを作成する
APIを作成する場合はレスポンス形式を統一することになると思うので、
下記のような抽象クラスを作成しておいたほうがよいでしょう。
[Laravel5.8 FormRequestでバリデーション失敗時にJsonResponseを返す - Qiita](https://qiita.com/ucan-lab/items/bbf1da23198201ef130b#apirequest)

# 参考記事
- [How to make a custom FormRequest error response in Laravel 5.5](https://laracasts.com/discuss/channels/laravel/how-make-a-custom-formrequest-error-response-in-laravel-55?page=1)
- [Laravel5.1.xでAPIを作る際に気になっていたことを調べました - Qiita](https://qiita.com/zaburo/items/f0db54bd3ebd81a8ce68)