Forms & validation
Validation rules are declared where your data lives, as attributes. Symfony ships with dozens of constraints: emails, URLs, ranges, IBANs, image dimensions... and you can create your own.
Now the fun part: #[MapRequestPayload] takes the JSON body of the request, deserializes it into your object and validates it. One attribute replaces all that boilerplate.
If validation fails, Symfony automatically returns a 422 response detailing every error. If it succeeds, your controller receives a perfectly typed object.
Building HTML forms instead of APIs? The Symfony Form component renders, processes and validates them reusing these same constraints, including protection against CSRF attacks.
<?phpnamespace App\Dto;use Symfony\Component\Validator\Constraints as Assert;class SignupRequest{ public function __construct( #[Assert\NotBlank] #[Assert\Email] public string $email, #[Assert\Length(min: 8)] public string $password, ) { }}<?phpnamespace App\Controller;use App\Dto\SignupRequest;use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;use Symfony\Component\HttpFoundation\JsonResponse;use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;use Symfony\Component\Routing\Attribute\Route;class SignupController extends AbstractController{ #[Route('/api/signup', methods: ['POST'])] public function signup( #[MapRequestPayload] SignupRequest $request, ): JsonResponse { // if you get here, the JSON payload was valid; otherwise, // Symfony already returned a 422 response with the errors return $this->json(['welcome' => $request->email]); }}