Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Validation

Validation rules in Symfony are written as attributes right above the entity's properties. A form applies them by itself: isValid() returns false, and error messages appear next to the fields.

Constraints

namespace App\Entity;
 
use Symfony\Component\Validator\Constraints as Assert;
 
class Post
{
    #[ORM\Column(length: 255)]
    #[Assert\NotBlank]
    #[Assert\Length(min: 5, max: 255)]
    private ?string $title = null;
 
    #[ORM\Column(type: Types::TEXT)]
    #[Assert\NotBlank]
    private ?string $body = null;
}

Nothing else is needed: the form from the previous lesson starts rejecting an empty or too short title.

Common constraints

ConstraintWhat it checks
NotBlankThe value is not empty
Length(min: 3, max: 50)String length
EmailAn email address
UrlA site address
Positive, PositiveOrZeroThe sign of a number
Range(min: 1, max: 100)A number within bounds
Choice(['draft', 'published'])One of the values
Regex('/^[a-z0-9-]+$/')A pattern match
Image(maxSize: '2M')The file is an image of at most 2 MB

Custom messages

#[Assert\NotBlank(message: 'Enter a title')]
#[Assert\Length(
    min: 5,
    minMessage: 'The title must be at least {{ limit }} characters long',
)]
private ?string $title = null;

Uniqueness

A check against the database is a constraint above the class:

use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
 
#[ORM\Entity]
#[UniqueEntity('email', message: 'This address is already taken')]
class User
{
    #[ORM\Column(unique: true)]
    #[Assert\Email]
    private ?string $email = null;
}

Validating by hand

Without a form, the ValidatorInterface service validates an object:

use Symfony\Component\Validator\Validator\ValidatorInterface;
 
public function import(ValidatorInterface $validator): Response
{
    $post = new Post();
    $post->setTitle('');
 
    $errors = $validator->validate($post);
 
    if (count($errors) > 0) {
        foreach ($errors as $error) {
            echo $error->getPropertyPath() . ': ' . $error->getMessage();
        }
    }
 
    // …
}

A rule that depends on several fields

use Symfony\Component\Validator\Context\ExecutionContextInterface;
 
#[Assert\Callback]
public function validateDates(ExecutionContextInterface $context): void
{
    if ($this->endsAt < $this->startsAt) {
        $context->buildViolation('The end is before the start')
            ->atPath('endsAt')
            ->addViolation();
    }
}

Common mistake

Most constraints let an empty value through: #[Assert\Email] treats an empty string and null as valid. If a field is required, add #[Assert\NotBlank] next to it. And remember: the required attribute in the markup is checked only by the browser and is easy to bypass — the real check is always on the server.

Thanks for reading!

Donate to the site — we work hard for you.