Skip to content

Sequentially

Edit this page

This constraint allows you to apply a set of rules that should be validated step-by-step, allowing you to interrupt the validation once the first violation is raised.

As an alternative in situations Sequentially cannot solve, you may consider using GroupSequence which allows more control.

Basic Usage

Suppose that you have a Place object with an $address property which must match the following requirements:

  • it's a non-blank string
  • of at least 10 chars long
  • with a specific format
  • and geolocalizable using an external service

In such situations, you may encounter three issues:

  • the Length or Regex constraints may fail hard with a UnexpectedValueException exception if the actual value is not a string, as enforced by Type.
  • you may end with multiple error messages for the same property.
  • you may perform a useless and heavy external call to geolocalize the address, while the format isn't valid.

You can validate each of these constraints sequentially to solve these issues:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// src/Localization/Place.php
namespace App\Localization;

use App\Validator\Constraints as AcmeAssert;
use Symfony\Component\Validator\Constraints as Assert;

class Place
{
    private const string ADDRESS_REGEX = '...';

    #[Assert\Sequentially([
        new Assert\NotNull,
        new Assert\Type('string'),
        new Assert\Length(min: 10),
        new Assert\Regex(Place::ADDRESS_REGEX),
        new AcmeAssert\Geolocalizable,
    ])]
    public string $address;

    // ...
}

Conditionally Cascading Validation

8.2

Support for the Valid constraint inside Sequentially was introduced in Symfony 8.2.

When a property can hold values of different types (e.g. user input that can be a string or an object), you may want to validate the nested object only after checking that it's an instance of the expected class. Applying the Type and Valid constraints separately doesn't work, because both always run. Instead, add Valid after Type inside Sequentially:

1
2
3
4
5
6
7
8
9
10
11
12
13
// src/Model/ImportRequest.php
namespace App\Model;

use Symfony\Component\Validator\Constraints as Assert;

class ImportRequest
{
    #[Assert\Sequentially([
        new Assert\Type(ResourceInput::class),
        new Assert\Valid(),
    ])]
    public mixed $resource = null;
}

If $resource is not a ResourceInput object, Type adds a violation and the sequence stops. Otherwise, Valid validates the constraints of that object. Always add Valid after the constraints that check the value type; if Valid runs first on a scalar value, an exception is thrown.

The nested Valid constraint can't define the groups option. Define it in Sequentially instead. These groups are also the ones used to validate the nested object. For example, with groups: ['import'], only the import constraints of the nested object run; add Default to that list to also run its Default constraints.

Valid can't be used directly inside other composite constraints (such as All or AtLeastOneOf). Instead, wrap it in a Sequentially constraint and add that to the other composite constraint.

Options

constraints

type: array

This required option is the array of validation constraints that you want to apply sequentially.

groups

type: array | string default: null

It defines the validation group or groups of this constraint. Read more about validation groups.

payload

type: mixed default: null

This option can be used to attach arbitrary domain-specific data to a constraint. The configured payload is not used by the Validator component, but its processing is completely up to you.

For example, you may want to use several error levels to present failed constraints differently in the front-end depending on the severity of the error.

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version