Skip to content

Commit 783fda7

Browse files
committed
[Validator] Add a constraint to sequentially validate a set of constraints
1 parent df3759b commit 783fda7

File tree

4 files changed

+155
-0
lines changed

4 files changed

+155
-0
lines changed

reference/constraints.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Validation Constraints Reference
6262
constraints/Isbn
6363
constraints/Issn
6464

65+
constraints/Sequentially
6566
constraints/Callback
6667
constraints/Expression
6768
constraints/All
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
Sequentially
2+
============
3+
4+
This constraint allows you to apply a collection of rules that should be
5+
validated step-by-step, allowing to interrupt the validation of further ones
6+
in the collection once the first violation is raised.
7+
8+
It's a simpler, but less powerful alternative to :doc:`GroupSequence</validation/sequence_provider>`.
9+
10+
.. versionadded:: 5.1
11+
12+
The ``Sequentially`` constraint was introduced in Symfony 5.1.
13+
14+
========== ===================================================================
15+
Applies to :ref:`property or method <validation-property-target>`
16+
Options - `constraints`_
17+
- `groups`_
18+
- `payload`_
19+
Class :class:`Symfony\\Component\\Validator\\Constraints\\Sequentially`
20+
Validator :class:`Symfony\\Component\\Validator\\Constraints\\SequentiallyValidator`
21+
========== ===================================================================
22+
23+
Basic Usage
24+
-----------
25+
26+
Suppose that you have a ``Place`` object with an ``$address`` property which
27+
must match following requirements:
28+
- it's a non-blank string
29+
- of at least 10 chars long
30+
- matching a specific format
31+
- and geolocalizable using an external service
32+
33+
In such situations, you may encounter three issues:
34+
- the ``Length`` or ``Regex`` constraints may fail hard with a :class:`Symfony\\Component\\Validator\\Exception\\UnexpectedValueException`
35+
exception if the actual value is not a string, as enforced by ``Type``.
36+
- You may end with multiple error messages for the same property
37+
- you may perform a useless & heavy external call to the service allowing you
38+
to geolocalize the address, while the format isn't valid.
39+
40+
You can validate each of these constraints sequentially to solve these issues:
41+
42+
.. configuration-block::
43+
44+
.. code-block:: php-annotations
45+
46+
// src/Localization/Place.php
47+
namespace App\Localization;
48+
49+
use App\Validator\Constraints as AcmeAssert;
50+
use Symfony\Component\Validator\Compound;
51+
use Symfony\Component\Validator\Constraints as Assert;
52+
53+
class Place
54+
{
55+
/**
56+
* @var string
57+
*
58+
* @Assert\Sequentially({
59+
* @Assert\NotBlank(),
60+
* @Assert\Type("string"),
61+
* @Assert\Length(min=10),
62+
* @Assert\Regex(Place::ADDRESS_REGEX),
63+
* @AcmeAssert\Geolocalizable(),
64+
* })
65+
*/
66+
public $address;
67+
}
68+
69+
.. code-block:: yaml
70+
71+
# config/validator/validation.yaml
72+
App\Localization\Place:
73+
properties:
74+
address:
75+
- Sequentially:
76+
- NotBlank: ~
77+
- Type: string
78+
- Length: { min: 10 }
79+
- Regex: !php/const Place::ADDRESS_REGEX
80+
- App\Validator\Constraints\Geolocalizable: ~
81+
82+
.. code-block:: xml
83+
84+
<!-- config/validator/validation.xml -->
85+
<?xml version="1.0" encoding="UTF-8" ?>
86+
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
87+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
88+
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
89+
90+
<class name="App\Localization\Place">
91+
<property name="address">
92+
<constraint name="Sequentially">
93+
<constraint name="NotBlank"/>
94+
<constraint name="Type">string</constraint>
95+
<constraint name="Length">
96+
<option name="min">10</option>
97+
</constraint>
98+
<constraint name="Regex">
99+
<option name="pattern">/address-regex/</option>
100+
</constraint>
101+
<constraint name="App\Validator\Constraints\Geolocalizable"/>
102+
</constraint>
103+
</property>
104+
</class>
105+
</constraint-mapping>
106+
107+
.. code-block:: php
108+
109+
// src/Localization/Place.php
110+
namespace App\Localization;
111+
112+
use App\Validator\Constraints as AcmeAssert;
113+
use Symfony\Component\Validator\Mapping\ClassMetadata;
114+
115+
class Place
116+
{
117+
public static function loadValidatorMetadata(ClassMetadata $metadata)
118+
{
119+
$metadata->addPropertyConstraint('address', new Assert\Sequentially([
120+
new Assert\NotBlank(),
121+
new Assert\Type("string"),
122+
new Assert\Length(['min' => 10]),
123+
new Assert\Regex(self::ADDRESS_REGEX),
124+
new AcmeAssert\Geolocalizable(),
125+
]));
126+
}
127+
}
128+
129+
Options
130+
-------
131+
132+
``constraints``
133+
~~~~~~~~~~~~~~~
134+
135+
**type**: ``array`` [:ref:`default option <validation-default-option>`]
136+
137+
This required option is the array of validation constraints that you want
138+
to apply sequentially.
139+
140+
.. include:: /reference/constraints/_groups-option.rst.inc
141+
142+
.. include:: /reference/constraints/_payload-option.rst.inc

reference/constraints/map.rst.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ Financial and other Number Constraints
8383
Other Constraints
8484
~~~~~~~~~~~~~~~~~
8585

86+
* :doc:`Sequentially </reference/constraints/Sequentially>`
8687
* :doc:`Callback </reference/constraints/Callback>`
8788
* :doc:`Expression </reference/constraints/Expression>`
8889
* :doc:`All </reference/constraints/All>`

validation/sequence_provider.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,3 +355,14 @@ provides a sequence of groups to be validated:
355355
// ...
356356
}
357357
}
358+
359+
How to Sequentially Apply Constraints on a Single Property
360+
==========================================================
361+
362+
In most simple cases, you may want to apply constraints sequentially on a single
363+
property. The :doc:`Sequentially constraint</reference/constraints/Sequentially>`
364+
can solve this for you in a simpler way than using a ``GroupSequence``.
365+
366+
.. versionadded:: 5.1
367+
368+
The ``Sequentially`` constraint was introduced in Symfony 5.1.

0 commit comments

Comments
 (0)