LaravelでFormRequest使ってJSONをValidateする って、どんな呪文だ?


Laravelって、気が付くとcontrollerがひどいことになるよね。MVCの宿命かもしれないけど。
今回はAPIの処理で受け取ったJSONのvalidationをFormRequestを使ってやってみるよ。
下記サイトをバリ参考にしました。ありがとうございます。
Laravelを使ったAPI開発でController内のバリデーションをFormRequestに抽出して幸せになろう

ちなみに環境は下記の通り
Laravel 7.13.0
OS Windows10
開発環境 homestead
DB mysql

1.チェックしたいJSON

こんな感じのデータをチェックするよ。

{       
    "company_id" : "12345"  ,
    "company_name" : "corp XXX",
    "branches" : [  
        {"no" : "1", 
         "branch_code" : "0123",
         "branch_name" : "ABC"
        }
        {"no" : "2", 
         "branch_code" : "9876",
         "branch_name" : "XYZ"
        }
    ]   
}

配列の中に更に配列があるパターンだね。
APIでこれをもらってきて、必須チェックとか数字チェックとかするよ。

2.考え方

イメージとして、controllerでJSONを受け取るとき、まさに受け取るところでValidationをかけます。
これは、受け取るときのパラメータの書き方を、通常の(Request \$request)から、(《FormRequestの名前》 \$request)にして、FormRequestを通ってきたものを使うようにすればよいのです。

contollerの例
public function store (JsonRequest $request)
{
    //普通に処理を書いていく
    //$reqesutはvalidation実行後のものになる

}

では、FormRequestを書いていこう。

3.FormRequestの中身

まずは例によってartisanでrequestを作成するよ。
今回は「JsonRequest」という名前のrequestを作成します。

php artisan make:request JsonRequest

そうすると、/app/Httpの下に「Requests」というフォルダーが作成され、その中に「JsonRequest.php」が作成される。

JsonRequest.php
<?php

namespace App\Http\Requests;

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

class JsonRequest 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()は認証関係のチェックを行う用のfunctionです。
認証なんて何もしないよ!という人はreturnをtrueに変更してください。でないと常にfalseが返ってしまい、いつまでも認証エラーです。

piblic function rules()の中に、validationのルールを書いていきます。
書き方は普通のlaravelのvalidationの書き方と同じです。
公式マニュアルはこちら
今回はこんな風に書いてみました。
companyテーブルは、そういうものがあるつもりで考えてね。

JsonRequest.php
<?php

namespace App\Http\Requests;

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

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

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            //
            'company_id' => 'exists:company,id',  //comapnyテーブルの存在チェック
            'company_code' => 'numeric',    //数字であること
            'branches.*.no' => ['numeric', 'distinct'],   //数字、かつ重複チェック
            'branches.*.branch_code' => 'numeric',    //数字であること
            ];
    }
}

普通はこれでいいんだけど、FormRequestは基本画面から呼ばれた前提で処理が作られており、エラーが発生したとき画面にエラーメッセージを返そうとします。
今回はAPIで受け取っているので、エラー情報の返却もJSONで返したい。
なので、レスポンスをJSONで返す処理を追加します。

JsonRequest.php

    //public function rulesの後とか、適当なところに追加

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

もし独自のチェックをしたいとか、validationでない処理もしたいとか考えているなら、public function withValidatorに$varidator->afterとか追加すれば実現できます。ここでは書かないけど。

ここまで出来たら、最初に書いたようにcontrollerにJsonRequestを追加してください。

JsonController.php
// 必要なところだけ抜粋
//最初にJsonRequestを取り込んでね
use App\Http\Requests\JsonRequest;

//途中省略

    public function store(JsonRequest $request)
    {
     //普通に処理を書く
     //validateはもう実行済み

    }

こうすると、あら不思議。勝手にvalidateされちゃいます。
メッセージの変更の仕方とかは、別の人の記事を探してね。

まずは、めでたしめでたし。