11Traverse
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.
1717Basic 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,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 =" 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+
95140Options
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/AppBundle/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+ # src/AppBundle/Resources/config/validation.yml
174+ AppBundle\Entity\BookCollection :
175+ constraints :
176+ - Traverse : false
177+
178+ .. code-block :: xml
179+
180+ <!-- src/AppBundle/Resources/config/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 =" AppBundle\Entity\BookCollection" >
187+ <constraint name =" Traverse" >false</constraint >
188+ </class >
189+ </constraint-mapping >
190+
191+ .. code-block :: php
192+
193+ // src/AppBundle/Entity/BookCollection.php
194+ namespace AppBundle\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
0 commit comments