Skip to content

Commit d670d53

Browse files
committed
[Validator] Fixed Traverse constraint reference
1 parent 2a475ba commit d670d53

File tree

1 file changed

+93
-24
lines changed

1 file changed

+93
-24
lines changed

reference/constraints/Traverse.rst

Lines changed: 93 additions & 24 deletions
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+
Objects 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/AppBundle/Entity/Book.php
27+
// src/AppBundle/Entity/BookCollection.php
3128
namespace AppBundle\Entity;
3229
30+
use Doctrine\Common\Collections\ArrayCollection;
31+
use Doctrine\Common\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="AppBundle\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="AppBundle\Entity\Editor")
53+
* @ORM\ManyToMany(targetEntity="AppBundle\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
6195
# src/AppBundle/Resources/config/validation.yml
62-
AppBundle\Entity\Book:
96+
AppBundle\Entity\BookCollection:
6397
constraints:
64-
- Symfony\Component\Validator\Constraints\Traverse: ~
98+
- Traverse: ~
6599
66100
.. code-block:: xml
67101
@@ -71,28 +105,63 @@ 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="AppBundle\Entity\Book">
75-
<constraint name="Symfony\Component\Validator\Constraints\Traverse"/>
108+
<class name="AppBundle\Entity\BookCollection">
109+
<constraint name="Traverse"/>
76110
</class>
77111
</constraint-mapping>
78112
79113
.. code-block:: php
80114
81-
// src/AppBundle/Entity/Book.php
115+
// src/AppBundle/Entity/BookCollection.php
82116
namespace AppBundle\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+
// src/AppBundle/Entity/BookCollection.php
155+
156+
// ... same as above
157+
158+
/**
159+
* ...
160+
* @Assert\Traverse(false)
161+
*/
162+
class BookCollection implements \IteratorAggregate
163+
{
164+
// ...
165+
}
166+
98167
.. include:: /reference/constraints/_payload-option.rst.inc

0 commit comments

Comments
 (0)