Validation
On the Forms page you saw that submitting a form calls
$form->isValid() before the data gets used, but that method doesn't invent
its rules out of nowhere. Symfony's Validator component reads a set of
constraints you declare directly on your Entity's properties, using plain
PHP 8 attributes. This page shows how those constraints are written, how the Form component
uses them automatically, and how to run the same checks manually outside of a form.
Constraints as PHP Attributes
Instead of a separate validation configuration file, constraints live as attributes right on
the property they protect — the rule and the data it governs sit in the same place. The
Assert namespace ships a large set of ready-made constraints; three of the most
common are shown below.
<?php
use Symfony\Component\Validator\Constraints as Assert;
#[Assert\NotBlank]
private string $title;
#[Assert\Length(min: 5, max: 255)]
private string $title;
#[Assert\Email]
private string $authorEmail;
NotBlank— rejects empty strings, empty arrays, andnull.Length— enforces a minimum and/or maximum character count.Email— checks the value looks like a valid email address.
Worked Example: The Article Entity
Combining several constraints on one Entity gives a complete picture. Here, Article
requires a title of a sensible length, a non-empty body, and a valid author email address —
all declared next to the property they protect.
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity]
class Article
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private int $id;
#[ORM\Column(length: 255)]
#[Assert\NotBlank]
#[Assert\Length(min: 5, max: 255)]
private string $title;
#[ORM\Column]
#[Assert\NotBlank]
private string $body;
#[ORM\Column]
#[Assert\NotBlank]
#[Assert\Email]
private string $authorEmail;
// getters and setters omitted for brevity
}
How This Ties Into the Form Component
When a form is bound to an Article and submitted, Symfony maps the request data
onto the Entity's properties and then automatically runs the Validator against every constraint
attribute it finds — this is exactly what happens under the hood of $form->isValid()
from the Forms page.
if ($form->isSubmitted() && $form->isValid()) {
// $article already satisfies every Assert constraint on its properties
$entityManager->persist($article);
$entityManager->flush();
}
Validating an Object Manually
You aren't limited to validating inside a form submission. Any service or controller can ask
the container for the ValidatorInterface and call validate() directly
on an object — handy for checking data that arrived from an API request, a console command, or
anywhere else outside the Form component.
<?php
use Symfony\Component\Validator\Validator\ValidatorInterface;
class ArticleController extends AbstractController
{
public function preview(ValidatorInterface $validator)
{
$article = new Article();
$article->setTitle('Hi');
$errors = $validator->validate($article);
if (count($errors) > 0) {
return $this->render('article/errors.html.twig', ['errors' => $errors]);
}
return $this->render('article/valid.html.twig');
}
}
Here $title is only two characters, so validate() returns a
ConstraintViolationList containing one violation for the Length
constraint — no form submission required to trigger the check.
Displaying Errors in a Twig Form
When validation runs as part of a form submission, each field's own errors can be rendered
right next to it with the form_errors() Twig function, keeping the message close
to the input the reader needs to fix.
{{ form_start(form) }}
{{ form_row(form.title) }}
{{ form_errors(form.title) }}
{{ form_row(form.body) }}
{{ form_errors(form.body) }}
{{ form_end(form) }}
ValidatorInterface — supplied to them automatically.