تکرار مکررات در اعتبار سنجی (Form Validation) ورودی ها در JsonApi

یکی از کارهای تکراری در نوشتن Api، اعتبار سنجی ورودی ها ست که به ازای هر Endpoint و به وسعت کل ورودی های مورد نظر باید انجام بشه. فریمورک لاراول برای این کنترل وروردی ها راه حل ساده و مختصری داره که این امکان این کار رو به اینصورت به شما میدهد:

$validatedData = $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ]);
    

داکیومنت خود لاراول کامل چگونگی استفاده رو نشون داده اما مشکل اینجاست که برای برگردوندن پیام مناسب برای هر endpoint باید در هر کنترلر exception اعتبار سنجی رو که از نوع ValidationException هست رو catch کنیم و خروجی مناسب را برگردانیم(انجام یک کار تکراری)

راه حل: از اونجایی که استاندار json api به ما میگوید که باید برای خطاهای از یک نوع مشابه در همه endpoint ها باید پیام یکسانی نمایش بدهیم، میتوانیم همه نوع خطای از این دست را قبل از رسیدن به کنترلر - و قبل از اینکه مجبور باشیم در هر Endpoint بطور مجزا catch کنیم - یکجا سازماندهی کنیم و خروجی رو بسازیم.

برای اینکار ابتدا در دایرکتوری app/Http/Requests/Traits یک فایل بسازید با نام UsesCustomErrorMessage.php

<?php

namespace App\Http\Requests\Traits;

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

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

این trait رو باید طوری بنویسید که مشابه signature اصلی اون باشه تا بجای متد اصلی خونده بشه حالا همینطور میتونید برای اینکه پیام ها و اینکه مربوط به کدوم فیلد میشوند رو از:

$validator->getMessageBag()->getMessages();

دریافت کنید و حالا برای استفاده از این trait میتونید اون رو در هر FormRequest ای استفاده کنید:

php artisan make:request StoreUserAccount

و در این فایل از trait استفاده کنید تا خطاهای ValidationException قبل از رسیدن به کنترلر برگردانده شوند:

<?php

namespace App\Http\Requests;

use App\Http\Requests\Traits\UsesCustomErrorMessage;
use Illuminate\Foundation\Http\FormRequest;

class StoreUserAccount extends FormRequest
{
    use UsesCustomErrorMessage;

    /**
     * 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 [
            'username' => 'required',
            'email' => 'required',
            'password' => 'required|confirmed',
        ];
    }

اما با اینکه مشکل تکرار گرفتن خطای ورودی رو حل کردیم اما هنوز باید برای هر کدام از کنترلرهامون تست اعتبارسنجی بنویسیم که این کار هم تکراری و وقت گیر خواهد بود...