FormRequest w Laravel służy głównie do walidiacji requestu z form - cóż za odkrywczość - powiecie. Niekiedy jednak nie posiadamy formy, a chcemy zwalidować sam request - jest na to prosta i sprawdzona metoda.

 

Sam przyznam o ile FormRequest'y są prostym i nawet zgrabnym sposobem do walidacji danych wejściowym (z form) to niestety do walidacji innych danych się słabo sprawdzają - zwłaszcza kiedy chcemy zwalidować czy dany zasób istnieje. Oczywiście można też pokusić się o walidację na samych route’ach, gdzie definiujemy endpointy naszej aplikacji (jednak za tym nie przepadam szczególnie - rozbija to logikę na różne miejsca i jest - kodowo - brzydkie).

 

Podstawową bolączką jest to, to co jest zwracane w razie nie poprawnej walidacji danych - wyjątek z błędnymi danymi (messageBag) z walidatora dotyczących poszczególnych pól. W momencie kiedy sprawdzamy jedynie czy dany zasób istnieje (po id, slug itd.) w razie braku takowego powinniśmy otrzymywać błąd 404 w reponse’ie [link do rfc].

 

Najlepszym sposobem obejścia tego jest utworzenie osobnego (abstrakcyjnego) FormRequest’a rozszerzającego tego dostarczanego z framework'iem i nadpisanie metody publicznej failedValidation - np. w taki sposób:

 

<?php

namespace App\Http\Requests;

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\App;
use Symfony\Component\HttpFoundation\Response;

abstract class AbstractHttpErrorFormRequest extends FormRequest
{
    protected int $httpErrorCode = Response::HTTP_NOT_FOUND;
    protected string $httpErrorMessage = 'Resource Not found';

    /** {@inheritDoc} */
    public function failedValidation(Validator $validator): void
    {
        App::abort($this->httpErrorCode, $this->httpErrorMessage);
    }
}

 

Potem już dane nasze FormRequest’y wymagającej takiej logiki wystarczy rozszerzać o przygotowaną przez nas klasę - i tylko tyle (lub aż tyle) :). Dodatkowo można pokusić się - jeżeli zajdzie oczywiście taka potrzeba - o przeciążenie pól na klasie $httpErrorCode oraz $httpErrorMessage i dostosowanie do danej potrzeby.

 

<?php

namespace App\Http\Requests\Article;

use App\Enums\Article\RequestParameterEnum;
use App\Http\Requests\AbstractHttpErrorFormRequest;

class CategoryRequest extends AbstractHttpErrorFormRequest
{
    /** {@inheritDoc} */
    public function authorize(): bool
    {
        return true;
    }

    /** {@inheritDoc} */
    public function rules(): array
    {
        return [
            RequestParameterEnum::categorySlug->value => [
                'required',
                'string',
                'exists:categories,slug',
            ],
        ];
    }
}

 

Oczywiście nie jest to idealne rozwiązanie - możemy napotkać bardziej skomplikowane walidacje, które będą wymagały od nas bardziej skomplikowanych działań (choć w tym miejscu zastanowiłbym się, czy nie robimy czegoś źle akurat - albo czy nie da się prościej podejść do problemu).

 

Konkluzja? Nie bójmy się nadpisywać, przeciążać, rozszerzać - na tym polega piękno sztuki programowania w końcu! :)