The Symfony Validator component provides over 80 built-in constraints to validate all kinds of data. Symfony 8.2 adds three new constraints for some common needs that previously required custom validation code.

EntityExists Constraint

Evgen Yurchenko
Contributed by Evgen Yurchenko in #63483

When your application receives entity identifiers in DTOs (e.g. from an API request or a form), you usually have to repeat the same logic: look up the entity, check if it exists and throw an error otherwise. The new EntityExists constraint handles this check and reports a missing entity as a validation violation on the property:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use App\Entity\Product;
use App\Entity\User;
use Symfony\Bridge\Doctrine\Validator\Constraints\EntityExists;
use Symfony\Component\Validator\Constraints as Assert;

final class AssignOrderCommand
{
    #[Assert\NotBlank]
    #[EntityExists(entityClass: User::class)]
    public string $userId;

    // look up the value by some field other than the entity identifier
    #[EntityExists(entityClass: Product::class, identifierField: 'sku')]
    public ?string $productSku = null;
}

By default, the value is looked up by the entity identifier. You can also use the repositoryMethod option to call your own repository method; any truthy result (an entity, a count, true, etc.) means that the entity exists.

Like most constraints, null and empty strings are considered valid, so combine it with NotBlank when the value is required.

Cron Constraint

Erfan Momeni
Contributed by Erfan Momeni in #64538

If your application lets users define schedules, a regular expression that only checks the format can miss invalid values such as 60 * * * * or */0 * * * *. The new Cron constraint validates cron expressions using a dedicated parser:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Symfony\Component\Validator\Constraints as Assert;

class Report
{
    // accepts standard expressions (e.g. '0 6 * * 1-5') and aliases like '@daily'
    #[Assert\Cron]
    public string $schedule;

    // only accepts standard 5-field expressions
    #[Assert\Cron(mode: Assert\Cron::MODE_STANDARD)]
    public string $strictSchedule;

    // also accepts the hashed expressions of the Scheduler component (e.g. '# # * * *')
    #[Assert\Cron(mode: Assert\Cron::MODE_HASHED)]
    public string $scheduledTask;
}

The MODE_HASHED mode also accepts hashed cron expressions such as # # * * * and aliases like #daily, which the Scheduler component uses to spread recurring tasks over time. This constraint requires the dragonmantank/cron-expression package, which is the same one used by the Scheduler component for cron triggers.

Audio Constraint

Valmont Pehaut-Pietri Nicolas Grekas
Contributed by Valmont Pehaut-Pietri and Nicolas Grekas in #66143

Symfony 7.4 added a Video constraint to validate video files. Symfony 8.2 adds its audio counterpart, the Audio constraint, which validates audio files based on their actual characteristics (duration, bitrate, sample rate, number of channels, codec and container) and not only on their MIME type and size:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Validator\Constraints as Assert;

class PodcastEpisode
{
    #[Assert\Audio(
        maxSize: '200M',
        // durations are in seconds, bitrates in bits per second and sample rates in Hz
        minDuration: 60,
        maxDuration: 3 * 3_600,
        minBitrate: 96_000,
        allowedSampleRates: [44_100, 48_000],
        maxChannels: 2,
        allowedCodecs: ['mp3', 'aac'],
        allowedContainers: ['mp3', 'mp4'],
    )]
    public ?File $audioFile = null;
}

The Audio constraint extends the File constraint, so all its options are available too. Its mimeTypes option defaults to audio/* and, unlike the Video constraint, it doesn't restrict codecs or containers unless you set the allowedCodecs and allowedContainers options. When you configure any of the audio-specific checks, it uses the Process component and the ffprobe binary from FFmpeg to inspect the file.

Published in #Living on the edge