Validating Requests on Symfony Framework
Validating Requests on Symfony Framework
Symfony Framework
Joubert RedRat Jul 19, 2023
https://github.com/joubertredrat/symfony-request-
validation
Basic example
Following the Symfony documentation, we just need to
create a class that will be used for mapping the values
from the request, as example below.
<?php declare(strict_types=1);
namespace App\Dto;
use App\Validator\CreditCard;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Positive;
use Symfony\Component\Validator\Constraints\Range;
use Symfony\Component\Validator\Constraints\Type;
class CreateTransactionDto
{
public function __construct(
#[NotBlank(message: 'I dont like this field empty')]
#[Type('string')]
public readonly string $firstName,
#[NotBlank()]
#[Type('string')]
#[CreditCard()]
public readonly string $cardNumber,
#[NotBlank()]
#[Positive()]
public readonly int $amount,
#[NotBlank()]
#[Type('int')]
#[Range(
min: 1,
max: 12,
notInRangeMessage: 'Expected to be between {{ min }}
)]
public readonly int $installments,
#[Type('string')]
public ?string $description = null,
) {
}
}
<?php declare(strict_types=1);
namespace App\Controller;
use App\Dto\CreateTransactionDto;
use DateTimeImmutable;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\Routing\Annotation\Route;
class TransactionController extends AbstractController
{
#[Route('/api/v1/transactions', name: 'app_api_create_transac
public function v1Create(#[MapRequestPayload] CreateTransacti
{
return $this->json([
'response' => 'ok',
'datetime' => (new DateTimeImmutable('now'))->format(
'firstName' => $createTransaction->firstName,
'lastName' => $createTransaction->lastName,
'amount' => $createTransaction->amount,
'installments' => $createTransaction->installments,
'description' => $createTransaction->description,
]);
}
}
{
"response": "ok",
"datetime": "2023-07-04 19:36:37",
"firstName": "Joubert",
"lastName": "RedRat",
"cardNumber": "4130731304267489",
"amount": 35011757,
"installments": 2,
"description": null
}
With this, we will create our abstract class, which will have
all code responsible to do the parse of request and
validation, as in the example below.
<?php declare(strict_types=1);
namespace App\Request;
use Jawira\CaseConverter\Convert;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Validator\Validator\ValidatorInterface;
<?php declare(strict_types=1);
namespace App\Request;
use App\Validator\CreditCard;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Positive;
use Symfony\Component\Validator\Constraints\Range;
use Symfony\Component\Validator\Constraints\Type;
#[NotBlank()]
#[Type('string')]
#[CreditCard()]
public readonly string $cardNumber;
#[NotBlank()]
#[Positive()]
public readonly int $amount;
#[NotBlank()]
#[Type('int')]
#[Range(
min: 1,
max: 12,
notInRangeMessage: 'Expected to be between {{ min }} and
)]
public readonly int $installments;
#[Type('string')]
public ?string $description = null;
}
<?php declare(strict_types=1);
namespace App\Controller;
use App\Request\CreateTransactionRequest;
use DateTimeImmutable;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
{
"errors": [
{
"property": "first_name",
"value": null,
"message": "I dont like this field empty."
},
{
"property": "card_number",
"value": "1130731304267489",
"message": "Expected valid credit card number."
},
{
"property": "amount",
"value": -4,
"message": "This value should be positive."
},
{
"property": "installments",
"value": 16,
"message": "Expected to be between 1 and 12, got 16"
}
]
}
{
"response": "ok",
"datetime": "2023-07-01 16:39:48",
"first_name": "Joubert",
"last_name": "RedRat",
"card_number": "4130731304267489",
"amount": 35011757,
"installments": 2,
"description": null
}
Limitations
I'm still looking for any option as a solution for being able
to use readonly in optional fields, like using Reflection for
example, and I accept suggestions.