Skip to content

Commit df08e97

Browse files
committed
Merge branch '5.0'
* 5.0: [Validator] Fixed `Traverse` constraint reference
2 parents bcb0c05 + 39323cc commit df08e97

File tree

2 files changed

+137
-26
lines changed

2 files changed

+137
-26
lines changed

reference/constraints/Traverse.rst

+136-25
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
Traverse
22
========
33

4-
Objects do not validate nested objects by default unless explicitly using
5-
this constraint.
6-
If only specific nested objects should be validated by cascade, consider
7-
using the :doc:`/reference/constraints/Valid` instead.
4+
Object properties are only validated if they are accessible, either by being
5+
public or having public accessor methods (e.g. a public getter).
6+
If your object needs to be traversed to validate its data, you can use this
7+
constraint.
88

99
+----------------+-------------------------------------------------------------------------------------+
1010
| Applies to | :ref:`class <validation-class-target>` |
@@ -17,51 +17,85 @@ using the :doc:`/reference/constraints/Valid` instead.
1717
Basic Usage
1818
-----------
1919

20-
In the following example, create three classes ``Book``, ``Author`` and
21-
``Editor`` that all have constraints on their properties. Furthermore,
22-
``Book`` stores an ``Author`` and an ``Editor`` instance that must be
23-
valid too. Instead of adding the ``Valid`` constraint to both fields,
24-
configure the ``Traverse`` constraint on the ``Book`` class.
20+
In the following example, create two classes ``BookCollection`` and ``Book``
21+
that all have constraints on their properties.
2522

2623
.. configuration-block::
2724

2825
.. code-block:: php-annotations
2926
30-
// src/Entity/Book.php
27+
// src/Entity/BookCollection.php
3128
namespace App\Entity;
3229
30+
use Doctrine\Collections\ArrayCollection;
31+
use Doctrine\Collections\Collection
3332
use Doctrine\ORM\Mapping as ORM;
3433
use Symfony\Component\Validator\Constraints as Assert;
3534
3635
/**
3736
* @ORM\Entity
3837
* @Assert\Traverse
3938
*/
40-
class Book
39+
class BookCollection implements \IteratorAggregate
4140
{
4241
/**
43-
* @var Author
42+
* @var string
4443
*
45-
* @ORM\ManyToOne(targetEntity="App\Entity\Author")
44+
* @ORM\Column
45+
*
46+
* @Assert\NotBlank
4647
*/
47-
protected $author;
48+
protected $name = '';
4849
4950
/**
50-
* @var Editor
51+
* @var Collection|Book[]
5152
*
52-
* @ORM\ManyToOne(targetEntity="App\Entity\Editor")
53+
* @ORM\ManyToMany(targetEntity="App\Entity\Book")
5354
*/
54-
protected $editor;
55+
protected $books;
5556
56-
// ...
57+
// some other properties
58+
59+
public function __construct()
60+
{
61+
$this->books = new ArrayCollection();
62+
}
63+
64+
// ... setter for name, adder and remover for books
65+
66+
// the name can be validated by calling the getter
67+
public function getName(): string
68+
{
69+
return $this->name;
70+
}
71+
72+
/**
73+
* @return \Generator|Book[] The books for a given author
74+
*/
75+
public function getBooksForAuthor(Author $author): iterable
76+
{
77+
foreach ($this->books as $book) {
78+
if ($book->isAuthoredBy($author)) {
79+
yield $book;
80+
}
81+
}
82+
}
83+
84+
// neither the method above nor any other specific getter
85+
// could be used to validated all nested books;
86+
// this object needs to be traversed to call the iterator
87+
public function getIterator()
88+
{
89+
return $this->books->getIterator();
90+
}
5791
}
5892
5993
.. code-block:: yaml
6094
61-
# config/validator/validation.yaml
62-
App\Entity\Book:
95+
# config/validator/validation.yml
96+
App\Entity\BookCollection:
6397
constraints:
64-
- Symfony\Component\Validator\Constraints\Traverse: ~
98+
- Traverse: ~
6599
66100
.. code-block:: xml
67101
@@ -71,28 +105,105 @@ configure the ``Traverse`` constraint on the ``Book`` class.
71105
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
72106
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
73107
74-
<class name="App\Entity\Book">
75-
<constraint name="Symfony\Component\Validator\Constraints\Traverse"/>
108+
<class name="App\Entity\BookCollection">
109+
<constraint name="Traverse"/>
76110
</class>
77111
</constraint-mapping>
78112
79113
.. code-block:: php
80114
81-
// src/Entity/Book.php
115+
// src/Entity/BookCollection.php
82116
namespace App\Entity;
83117
84118
use Symfony\Component\Validator\Constraints as Assert;
85119
use Symfony\Component\Validator\Mapping\ClassMetadata;
86120
87-
class Book
121+
class BookCollection
88122
{
123+
// ...
124+
89125
public static function loadValidatorMetadata(ClassMetadata $metadata)
90126
{
91127
$metadata->addConstraint(new Assert\Traverse());
92128
}
93129
}
94130
131+
When the object implements ``\Traversable`` (like here with its child
132+
``\IteratorAggregate``), its traversal strategy will implicitly be set and the
133+
object will be iterated over without defining the constraint.
134+
It's mostly useful to add it to be explicit or to disable the traversal using
135+
the ``traverse`` option.
136+
If a public getter exists to return the inner books collection like
137+
``getBooks(): Collection``, the :doc:`/reference/constraints/Valid` constraint
138+
can be used on the ``$books`` property instead.
139+
95140
Options
96141
-------
97142

143+
The ``groups`` option is not available for this constraint.
144+
145+
146+
``traverse``
147+
~~~~~~~~~~~~
148+
149+
**type**: ``bool`` **default**: ``true``
150+
151+
Instances of ``\Traversable`` are traversed by default, use this option to
152+
disable validating:
153+
154+
.. configuration-block::
155+
156+
.. code-block:: php-annotations
157+
158+
// src/Entity/BookCollection.php
159+
160+
// ... same as above
161+
162+
/**
163+
* ...
164+
* @Assert\Traverse(false)
165+
*/
166+
class BookCollection implements \IteratorAggregate
167+
{
168+
// ...
169+
}
170+
171+
.. code-block:: yaml
172+
173+
# config/validator/validation.yml
174+
App\Entity\BookCollection:
175+
constraints:
176+
- Traverse: false
177+
178+
.. code-block:: xml
179+
180+
<!-- config/validator/validation.xml -->
181+
<?xml version="1.0" encoding="UTF-8" ?>
182+
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
183+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
184+
xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping https://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
185+
186+
<class name="App\Entity\BookCollection">
187+
<constraint name="Traverse">false</constraint>
188+
</class>
189+
</constraint-mapping>
190+
191+
.. code-block:: php
192+
193+
// src/Entity/BookCollection.php
194+
namespace App\Entity;
195+
196+
use Symfony\Component\Validator\Constraints as Assert;
197+
use Symfony\Component\Validator\Mapping\ClassMetadata;
198+
199+
class BookCollection
200+
{
201+
// ...
202+
203+
public static function loadValidatorMetadata(ClassMetadata $metadata)
204+
{
205+
$metadata->addConstraint(new Assert\Traverse(false));
206+
}
207+
}
208+
98209
.. include:: /reference/constraints/_payload-option.rst.inc

reference/constraints/Valid.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,6 @@ traverse
255255

256256
**type**: ``boolean`` **default**: ``true``
257257

258-
If this constraint is applied to a ``Traversable``, then all containing values
258+
If this constraint is applied to a ``\Traversable``, then all containing values
259259
will be validated if this option is set to ``true``. This option is ignored on
260260
arrays: Arrays are traversed in either case. Keys are not validated.

0 commit comments

Comments
 (0)