Validation
Validation rules are declared where your data lives, as attributes. Symfony ships with dozens of constraints: emails, URLs, 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.
Remember the signup form from the previous lesson? It binds to this same SignupRequest object, so submitting the form checks these same constraints. Define your rules once and reuse them in your HTML forms and your APIs.
<?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 SignupApiController 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]); }}