INTRODUÇÃO
É possível usar no Accelero (v2.17.0 em diante) um sistema de "request validados", parecido com o sistema de validação de requests do Laravel.
Com isso é possível tirar do Controller a lógica de validação de requests e deixar apenas a lógica de negócio, separando as responsabilidades.
Você pode validar o que quiser fora do Controller, mas a sugestão é:
- validar se todos os campos esperados no request estão presentes;
- validar se o usuário tem permissão pra executar a ação.
Pra criar um sistema desse tipo, são três etapas:
VALIDATED REQUEST
Criar uma classe pra representar o "request validado". Ela deve ter uma estrutura parecida com:
<?php
namespace Accelero\Controllers\FormRequests;
use Iongrade\Exceptions\InvalidRequestException;
use Iongrade\Exceptions\NoPermissionException;
use Laminas\Diactoros\ServerRequest;
class ClasseXYZSaveFormRequest extends ServerRequest {
public readonly string $foo;
public readonly int $bar;
/**
* @throws NoPermissionException
* @throws InvalidRequestException
*/
public function __construct(array $serverParams = [], array $uploadedFiles = [], $uri = null, ?string $method = null, $body = 'php://input', array $headers = [], array $cookieParams = [], array $queryParams = [], $parsedBody = null, string $protocol = '1.1') {
parent::__construct($serverParams, $uploadedFiles, $uri, $method, $body, $headers, $cookieParams, $queryParams, $parsedBody, $protocol);
$this->validate();
}
/**
* @throws InvalidRequestException
* @throws NoPermissionException
*/
private function validate() : void {
$parsedBody = $this->getParsedBody();
if (!is_array($parsedBody)) throw new InvalidRequestException('Request body did not provide proper array');
if (null === ($parsedBody['foo'] ?? null)) throw new InvalidRequestException();
if (null === ($parsedBody['bar'] ?? null)) throw new InvalidRequestException();
if (false === ClasseXYZ::checkPermissionStatic('save')) throw new NoPermissionException();
$this->foo = $parsedBody['foo'];
$this->bar = $parsedBody['bar'];
}
}
Note que:
- foi chamado o construtor do parent;
- foram feitas validações de campos presentes;
- foi feita checagem de permissão fazendo referência à
ClasseXYZ; - foram criados alguns campos
fooebarpra facilitar o uso mais pra frente.
MIDDLEWARE
É necessário criar um middleware simples que vai ser responsável por criar o request validado. Estrutura básica:
<?php
namespace Accelero\Controllers\Middleware;
use Accelero\Controllers\FormRequests\ClasseXYZSaveFormRequest;
use Accelero\Controllers\FormRequests\FormRequestFactory;
use Exception;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class ClasseXYZSaveMiddleware implements MiddlewareInterface {
/**
* @throws Exception
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface {
$request = FormRequestFactory::createFromType(ClasseXYZSaveFormRequest::class);
return $handler->handle($request);
}
}
ROTA
É necessário especificar, na rota, que o Middleware que acabamos de criar deve ser utilizado:
<?php
use Accelero\Controllers\ClassexYZController;
use Accelero\Controllers\Middleware\ClasseXYZSaveMiddleware;
use Accelero\Controllers\Middleware\LoggedInMiddleware;
use Accelero\Router\Route;
return [
new Route('*', '/classeXYZ/save', [ClassexYZController::class, 'myAction'], middleware: [LoggedInMiddleware::class, ClasseXYZSaveMiddleware::class]),
];
CONTROLLER
Finalmente, devemos especificar no nosso Controller que receberemos um request específico validado (mas nem é obrigatório fazer isso, já que o request validade extende o ServerRequestInterface -- apenas pode ajudar porque você pode aproveitar campos específicos que foram setados).
<?php
namespace Accelero\Controllers;
use Accelero\Controllers\FormRequests\ClasseXYZSaveFormRequest;
use Psr\Http\Message\ResponseInterface;
use SinFramework\DataFlusher\DataFlusher;
class ClasseXYZController {
public function myAction(ClasseXYZSaveFormRequest $request) : ResponseInterface {
$message = $request->foo . ' >> ' . $request->bar;
return DataFlusher::flush($request, DataFlusher::HTTP_OK, $message);
}
}
PRECISA DISSO?
Não.
Se você usar o "modo padrão" do roteador Route (https://route.thephpleague.com/), tudo vai funcionar. Na verdade, o sistema praticamente inteiro funciona sem isso, e todos os requests são "genéricos e validados diretamente no Controller".
Mas pode ajudar, principalmente:
- pra separar as responsabilidades dos componentes;
- pra simplificar os controller;
- pra que o sistema tenha um pouco mais "cara de mercado/Laravel", onde esse paradigma é comum.