Skip to content

Commit 29a6194

Browse files
committed
[Validator] Add a constraint to sequentially validate a set of constraints
1 parent 8bf1f5e commit 29a6194

File tree

4 files changed

+155
-0
lines changed

4 files changed

+155
-0
lines changed

reference/constraints.rst

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ Validation Constraints Reference
6363
constraints/Isbn
6464
constraints/Issn
6565

66+
constraints/Sequentially
6667
constraints/Compound
6768
constraints/Callback
6869
constraints/Expression
+142
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
Sequentially
2+
============
3+
4+
This constraint allows you to apply a set of rules that should be validated
5+
step-by-step, allowing to interrupt the validation once the first violation is raised.
6+
7+
As an alternative in situations ``Sequentially`` cannot solve, you may consider
8+
using :doc:`GroupSequence</validation/sequence_provider>` which allows more control.
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 the following requirements:
28+
- it's a non-blank string
29+
- of at least 10 chars long
30+
- with 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 and heavy external call to geolocalize the address,
38+
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\Constraints as Assert;
51+
52+
class Place
53+
{
54+
/**
55+
* @var string
56+
*
57+
* @Assert\Sequentially({
58+
* @Assert\NotBlank(),
59+
* @Assert\Type("string"),
60+
* @Assert\Length(min=10),
61+
* @Assert\Regex(Place::ADDRESS_REGEX),
62+
* @AcmeAssert\Geolocalizable(),
63+
* })
64+
*/
65+
public $address;
66+
}
67+
68+
.. code-block:: yaml
69+
70+
# config/validator/validation.yaml
71+
App\Localization\Place:
72+
properties:
73+
address:
74+
- Sequentially:
75+
- NotBlank: ~
76+
- Type: string
77+
- Length: { min: 10 }
78+
- Regex: !php/const App\Localization\Place::ADDRESS_REGEX
79+
- App\Validator\Constraints\Geolocalizable: ~
80+
81+
.. code-block:: xml
82+
83+
<!-- config/validator/validation.xml -->
84+
<?xml version="1.0" encoding="UTF-8" ?>
85+
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
86+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
87+
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
88+
89+
<class name="App\Localization\Place">
90+
<property name="address">
91+
<constraint name="Sequentially">
92+
<constraint name="NotBlank"/>
93+
<constraint name="Type">string</constraint>
94+
<constraint name="Length">
95+
<option name="min">10</option>
96+
</constraint>
97+
<constraint name="Regex">
98+
<option name="pattern">/address-regex/</option>
99+
</constraint>
100+
<constraint name="App\Validator\Constraints\Geolocalizable"/>
101+
</constraint>
102+
</property>
103+
</class>
104+
</constraint-mapping>
105+
106+
.. code-block:: php
107+
108+
// src/Localization/Place.php
109+
namespace App\Localization;
110+
111+
use App\Validator\Constraints as AcmeAssert;
112+
use Symfony\Component\Validator\Constraints as Assert;
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

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ Financial and other Number Constraints
8484
Other Constraints
8585
~~~~~~~~~~~~~~~~~
8686

87+
* :doc:`Sequentially </reference/constraints/Sequentially>`
8788
* :doc:`Compound </reference/constraints/Compound>`
8889
* :doc:`Callback </reference/constraints/Callback>`
8990
* :doc:`Expression </reference/constraints/Expression>`

validation/sequence_provider.rst

+11
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+
Sometimes, 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 more straightforward 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)