8000 [Validator] Wrapped collections into CollectionNode instances · symfony/symfony@117b1b9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 117b1b9

Browse files
committed
[Validator] Wrapped collections into CollectionNode instances
1 parent 94583a9 commit 117b1b9

File tree

3 files changed

+169
-59
lines changed

3 files changed

+169
-59
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Validator\Mapping;
13+
14+
/**
15+
* @since %%NextVersion%%
16+
* @author Bernhard Schussek <bschussek@gmail.com>
17+
*/
18+
class CollectionMetadata implements MetadataInterface
19+
{
20+
private $traversalStrategy;
21+
22+
public function __construct($traversalStrategy)
23+
{
24+
$this->traversalStrategy = $traversalStrategy;
25+
}
26+
27+
/**
28+
* Returns all constraints for a given validation group.
29+
*
30+
* @param string $group The validation group.
31+
*
32+
* @return \Symfony\Component\Validator\Constraint[] A list of constraint instances.
33+
*/
34+
public function findConstraints($group)
35+
{
36+
return array();
37+
}
38+
39+
public function getCascadingStrategy()< E29B /div>
40+
{
41+
return CascadingStrategy::NONE;
42+
}
43+
44+
public function getTraversalStrategy()
45+
{
46+
return $this->traversalStrategy;
47+
}
48+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Validator\Node;
13+
14+
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
15+
use Symfony\Component\Validator\Mapping\MetadataInterface;
16+
17+
/**
18+
* Represents an traversable collection in the validation graph.
19+
*
20+
* @since 2.5
21+
* @author Bernhard Schussek <bschussek@gmail.com>
22+
*/
23+
class CollectionNode extends Node
24+
{
25+
/**
26+
* Creates a new collection node.
27+
*
28+
* @param array|\Traversable $collection The validated collection
29+
* @param MetadataInterface $metadata The class metadata of that
30+
* object
31+
* @param string $propertyPath The property path leading
32+
* to this node
33+
* @param string[] $groups The groups in which this
34+
* node should be validated
35+
* @param string[]|null $cascadedGroups The groups in which
36+
* cascaded objects should be
37+
* validated
38+
*
39+
* @throws UnexpectedTypeException If the given value is not an array or
40+
* an instance of {@link \Traversable}
41+
*/
42+
public function __construct($collection, MetadataInterface $metadata, $propertyPath, array $groups, $cascadedGroups = null)
43+
{
44+
if (!is_array($collection) && !$collection instanceof \Traversable) {
45+
throw new UnexpectedTypeException($collection, 'object');
46+
}
47+
48+
parent::__construct(
49+
$collection,
50+
$metadata,
51+
$propertyPath,
52+
$groups,
53+
$cascadedGroups
54+
);
55+
}
56+
}

src/Symfony/Component/Validator/NodeTraverser/NodeTraverser.php

Lines changed: 65 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
use Symfony\Component\Validator\Exception\NoSuchMetadataException;
1717
use Symfony\Component\Validator\Mapping\CascadingStrategy;
1818
use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
19+
use Symfony\Component\Validator\Mapping\CollectionMetadata;
1920
use Symfony\Component\Validator\Mapping\TraversalStrategy;
2021
use Symfony\Component\Validator\MetadataFactoryInterface;
2122
use Symfony\Component\Validator\Node\ClassNode;
23+
use Symfony\Component\Validator\Node\CollectionNode;
2224
use Symfony\Component\Validator\Node\Node;
2325
use Symfony\Component\Validator\Node\PropertyNode;
2426
use Symfony\Component\Validator\NodeVisitor\NodeVisitorInterface;
@@ -84,6 +86,8 @@ public function traverse(array $nodes, ExecutionContextInterface $context)
8486

8587
if ($node instanceof ClassNode) {
8688
$this->traverseClassNode($node, $traversal);
89+
} elseif ($node instanceof CollectionNode) {
90+
$this->traverseCollectionNode($node, $traversal);
8791
} else {
8892
$this->traverseNode($node, $traversal);
8993
}
@@ -145,13 +149,12 @@ private function traverseNode(Node $node, Traversal $traversal)
145149
// Arrays are always traversed, independent of the specified
146150
// traversal strategy
147151
// (BC with Symfony < 2.5)
148-
$this->cascadeEachObjectIn(
152+
$traversal->nodeQueue->enqueue(new CollectionNode(
149153
$node->value,
154+
new CollectionMetadata($traversalStrategy),
150155
$node->propertyPath,
151-
$cascadedGroups,
152-
$traversalStrategy,
153-
$traversal
154-
);
156+
$cascadedGroups
157+
));
155158

156159
return;
157160
}
@@ -188,13 +191,13 @@ private function traverseNode(Node $node, Traversal $traversal)
188191
));
189192
}
190193

191-
$this->cascadeEachObjectIn(
194+
$traversal->nodeQueue->enqueue(new CollectionNode(
192195
$node->value,
196+
new CollectionMetadata($traversalStrategy),
193197
$node->propertyPath,
194-
$cascadedGroups,
195-
$traversalStrategy,
196-
$traversal
197-
);
198+
$node->groups,
199+
$node->cascadedGroups
200+
));
198201
}
199202

200203
private function traverseClassNode(ClassNode $node, Traversal $traversal, $traversalStrategy = TraversalStrategy::IMPLICIT)
@@ -252,54 +255,23 @@ private function traverseClassNode(ClassNode $node, Traversal $traversal, $trave
252255
));
253256
}
254257

255-
$this->cascadeEachObjectIn(
258+
$traversal->nodeQueue->enqueue(new CollectionNode(
256259
$node->value,
260+
new CollectionMetadata($traversalStrategy),
257261
$node->propertyPath,
258262
$node->groups,
259-
$traversalStrategy,
260-
$traversal
261-
);
263+
$node->cascadedGroups
264+
));
262265
}
263266

264-
private function cascadeObject($object, $propertyPath, array $groups, $traversalStrategy, Traversal $traversal)
267+
private function traverseCollectionNode(CollectionNode $node, Traversal $traversal)
265268
{
266-
try {
267-
$classMetadata = $this->metadataFactory->getMetadataFor($object);
268-
269-
if (!$classMetadata instanceof ClassMetadataInterface) {
270-
// error
271-
}
272-
273-
$traversal->nodeQueue->enqueue(new ClassNode(
274-
$object,
275-
$classMetadata,
276-
$propertyPath,
277-
$groups
278-
));
279-
} catch (NoSuchMetadataException $e) {
280-
// Rethrow if the TRAVERSE bit is not set
281-
if (!($traversalStrategy & TraversalStrategy::TRAVERSE)) {
282-
throw $e;
283-
}
284-
285-
// Rethrow if the object does not implement Traversable
286-
if (!$object instanceof \Traversable) {
287-
throw $e;
288-
}
289-
290-
// In that case, iterate the object and cascade each entry
291-
$this->cascadeEachObjectIn(
292-
$object,
293-
$propertyPath,
294-
$groups,
295-
$traversalStrategy,
296-
$traversal
297-
);
269+
if (false === $this->visit($node, $traversal->context)) {
270+
return;
298271
}
299-
}
300272

301-
private function cascadeEachObjectIn($collection, $propertyPath, array $groups, $traversalStrategy, Traversal $traversal)
302-
{
273+
$traversalStrategy = $node->metadata->getTraversalStrategy();
274+
303275
if ($traversalStrategy & TraversalStrategy::RECURSIVE) {
304276
// Try to traverse nested objects, but ignore if they do not
305277
// implement Traversable
@@ -311,18 +283,17 @@ private function cascadeEachObjectIn($collection, $propertyPath, array $groups,
311283
$traversalStrategy = TraversalStrategy::IMPLICIT;
312284
}
313285

314-
foreach ($collection as $key => $value) {
286+
foreach ($node->value as $key => $value) {
315287
if (is_array($value)) {
316288
// Arrays are always cascaded, independent of the specified
317289
// traversal strategy
318290
// (BC with Symfony < 2.5)
319-
$this->cascadeEachObjectIn(
291+
$traversal->nodeQueue->enqueue(new CollectionNode(
320292
$value,
321-
$propertyPath.'['.$key.']',
322-
$groups,
323-
$traversalStrategy,
324-
$traversal
325-
);
293+
new CollectionMetadata($traversalStrategy),
294+
$node->propertyPath.'['.$key.']',
295+
$node->groups
296+
));
326297

327298
continue;
328299
}
@@ -332,12 +303,47 @@ private function cascadeEachObjectIn($collection, $propertyPath, array $groups,
332303
if (is_object($value)) {
333304
$this->cascadeObject(
334305
$value,
335-
$propertyPath.'['.$key.']',
336-
$groups,
306+
$node->propertyPath.'['.$key.']',
307+
$node->groups,
337308
$traversalStrategy,
338309
$traversal
339310
);
340311
}
341312
}
342313
}
314+
315+
private function cascadeObject($object, $propertyPath, array $groups, $traversalStrategy, Traversal $traversal)
316+
{
317+
try {
318+
$classMetadata = $this->metadataFactory->getMetadataFor($object);
319+
320+
if (!$classMetadata instanceof ClassMetadataInterface) {
321+
// error
322+
}
323+
324+
$traversal->nodeQueue->enqueue(new ClassNode(
325+
$object,
326+
$classMetadata,
327+
$propertyPath,
328+
$groups
329+
));
330+
} catch (NoSuchMetadataException $e) {
331+
// Rethrow if the TRAVERSE bit is not set
332+
if (!($traversalStrategy & TraversalStrategy::TRAVERSE)) {
333+
throw $e;
334+
}
335+
336+
// Rethrow if the object does not implement Traversable
337+
if (!$object instanceof \Traversable) {
338+
throw $e;
339+
}
340+
341+
$traversal->nodeQueue->enqueue(new CollectionNode(
342+
$object,
343+
new CollectionMetadata($traversalStrategy),
344+
$propertyPath,
345< 4885 span class="diff-text-marker">+
$groups
346+
));
347+
}
348+
}
343349
}

0 commit comments

Comments
 (0)
0