Skip to content

Instantly share code, notes, and snippets.

@zabaala
Last active November 27, 2022 15:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zabaala/2e7e766c4ae5b0a97d2b71e1179e5fc3 to your computer and use it in GitHub Desktop.
Save zabaala/2e7e766c4ae5b0a97d2b71e1179e5fc3 to your computer and use it in GitHub Desktop.
How to validate domain DTOs using Laravel Framework avoiding use of FormRequest approaches.
<?php
/**
* A Simple way to use a DTO and your validations instead of a Laravel FormRequest approach.
*/
Route::post('/test', function (\Core\Comparison\Application\DTOs\SampleInput $input) {
// After a validation, should be possible retrieve a
// instance of input with all your properties filled.
return dd($input);
});
{
"id": 1,
"firstname": "Mauricio",
"lastname": "Rodrigues",
"age": 37 // property/value will be ignored. Continue reading the codes to understand why.
}
<?php
namespace Core\SomeDomain\Application\DTOs;
use Exception;
use Illuminate\Contracts\Validation\ValidatesWhenResolved;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
class SampleInput implements ValidatesWhenResolved
{
/**
* Notes:
* Properties should be nullables, can be promoted, but can't be readonly,
* because all there will be filled after the call of __constructor method.
* Considering that we don't want to create a package to solve this issue, these considerations
* are acceptable. But we need to check the clean architecture principles to know if some one of these criteria
* represent a pattern violation.
*
*
* @param string|null $id
* @param string|null $firstname
* @param string|null $lastname
*/
public function __construct(
public ?string $id = null,
public ?string $firstname = null,
public ?string $lastname = null
) {}
/**
* This method will be called by kernel on the request live cycle.
* I believe that just with it is possible to validate a DTO using annotations or attributes validations,
* without need to change anything in some low level of the framework, without create, by example, a package
* to do that.
*
* As I said, I believe, but I don't have a confirmation of that.
*
* I'll continue to trying implements a solutions for that.
*
* @return void
* @throws Exception
*/
public function validateResolved(): void
{
$properties = get_object_vars($this);
foreach ($properties as $property => $value) {
$this->{$property} = request($property);
}
// validate this DTO...
// $validator = app()->get(ValidatorInterface::class); // implement as you want...
// $validator->validate($this); if invalid, throw an Exception to the request.
// Since here, to continue, comment code bellow or, to see an error, leave the codes uncommented.
// create an error list...
$errors = [
'id' => 'Invalid ID for some reason...',
'firstname' => 'Firstname cannot be blank.',
'lastname' => 'Lastname must have at least 3 characters.',
];
/**
* ...and throws some HttpException interface.
* For the moment, we don't have a full control about the response structure.
* But we need to investigate how to change the response body to send back our errors properly.
*
* I've an idea that we can intercept here the request and apply change to him.
*/
throw new class(json_encode($errors)) extends \Exception implements HttpExceptionInterface
{
public function getStatusCode(): int
{
return 403;
}
public function getHeaders(): array
{
return ['Content-type' => 'application/problem+json'];
}
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment