Skip to content

Commit 1f7be1f

Browse files
mrcmoralesSimperfit
authored andcommitted
[Validator] added improve support for collection validation
1 parent fba11b4 commit 1f7be1f

19 files changed

+1884
-48
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
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\Constraints;
13+
14+
use Symfony\Component\Validator\Constraint;
15+
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
16+
17+
/**
18+
* @Annotation
19+
*
20+
* @author Marc Morales Valldepérez <marcmorales83@gmail.com>
21+
* @author Marc Morera Merino <yuhu@mmoreram.com>
22+
*/
23+
abstract class AbstractComposite extends Constraint
24+
{
25+
/**
26+
* @var array
27+
*
28+
* Set of constraints
29+
*/
30+
public $constraints = [];
31+
32+
/**
33+
* {@inheritdoc}
34+
*/
35+
public function __construct($options = null)
36+
{
37+
parent::__construct($options);
38+
39+
if (!\is_array($this->constraints)) {
40+
$this->constraints = [$this->constraints];
41+
}
42+
43+
// We consider explicid groups are defined if are not default one
44+
$areExplicitGroupsDefined = ($this->groups != [self::DEFAULT_GROUP]);
45+
46+
// Each constraint contained
47+
foreach ($this->constraints as $constraint) {
48+
if (!$constraint instanceof Constraint) {
49+
throw new ConstraintDefinitionException(sprintf('The value %s is not an instance of Constraint in constraint %s', $constraint, __CLASS__));
50+
}
51+
52+
if ($constraint instanceof Valid) {
53+
throw new ConstraintDefinitionException(sprintf('The constraint Valid cannot be nested inside constraint %s. You can only declare the Valid constraint directly on a field or method.', __CLASS__));
54+
}
55+
56+
// If explicid groups are defined
57+
if ($areExplicitGroupsDefined) {
58+
/*
59+
* If constraint has explicid groups defined
60+
*
61+
* In that case, the groups of the nested constraint need to be
62+
* a subset of the groups of the outer constraint.
63+
*/
64+
if ($constraint->groups !== [self::DEFAULT_GROUP]) {
65+
// If are not a subset
66+
if ($constraint->groups != array_intersect($constraint->groups, $this->groups)) {
67+
throw new ConstraintDefinitionException(sprintf('The groups defined in Constraint %s must be a subset of the groups defined in the Constraint %s', $constraint, __CLASS__));
68+
}
69+
70+
// Otherwise, we add all defined groups here
71+
} else {
72+
foreach ($this->groups as $group) {
73+
$constraint->addImplicitGroupName($group);
74+
}
75+
}
76+
77+
/*
78+
* Otherwise, we merge current groups with constraint
79+
*/
80+
} else {
81+
$this->groups = array_unique(array_merge($this->groups, $constraint->groups));
82+
}
83+
}
84+
}
85+
86+
/**
87+
* Adds the given group if this constraint is in the Default group.
88+
*
89+
* Also propagate same method to nested Constraints.
90+
*
91+
* @param string $group
92+
*
93+
* @api
94+
*/
95+
public function addImplicitGroupName($group)
96+
{
97+
parent::addImplicitGroupName($group);
98+
99+
foreach ($this->constraints as $constraint) {
100+
$constraint->addImplicitGroupName($group);
101+
}
102+
}
103+
104+
/**
105+
* {@inheritdoc}
106+
*/
107+
public function getDefaultOption()
108+
{
109+
return 'constraints';
110+
}
111+
112+
/**
113+
* {@inheritdoc}
114+
*/
115+
public function getRequiredOptions()
116+
{
117+
return ['constraints'];
118+
}
119+
}

src/Symfony/Component/Validator/Constraints/All.php

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,12 @@
1616
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
1717
*
1818
* @author Bernhard Schussek <bschussek@gmail.com>
19+
*
20+
* @api
21+
*
22+
* @deprecated Deprecated in 4.3, to be removed in 5.0. Use
23+
* {@link \Symfony\Component\Validator\Constraints\Each} instead.
1924
*/
20-
class All extends Composite
25+
class All extends Each
2126
{
22-
public $constraints = [];
23-
24-
public function getDefaultOption()
25-
{
26-
return 'constraints';
27-
}
28-
29-
public function getRequiredOptions()
30-
{
31-
return ['constraints'];
32-
}
33-
34-
protected function getCompositeOption()
35-
{
36-
return 'constraints';
37-
}
3827
}

src/Symfony/Component/Validator/Constraints/AllValidator.php

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,39 +11,14 @@
1111

1212
namespace Symfony\Component\Validator\Constraints;
1313

14-
use Symfony\Component\Validator\Constraint;
15-
use Symfony\Component\Validator\ConstraintValidator;
16-
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
17-
use Symfony\Component\Validator\Exception\UnexpectedValueException;
18-
1914
/**
2015
* @author Bernhard Schussek <bschussek@gmail.com>
16+
*
17+
* @api
18+
*
19+
* @deprecated Deprecated in 4.3, to be removed in 5.0. Use
20+
* {@link \Symfony\Component\Validator\Constraints\EachValidator} instead.
2121
*/
22-
class AllValidator extends ConstraintValidator
22+
class AllValidator extends EachValidator
2323
{
24-
/**
25-
* {@inheritdoc}
26-
*/
27-
public function validate($value, Constraint $constraint)
28-
{
29-
if (!$constraint instanceof All) {
30-
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\All');
31-
}
32-
33-
if (null === $value) {
34-
return;
35-
}
36-
37-
if (!\is_array($value) && !$value instanceof \Traversable) {
38-
throw new UnexpectedValueException($value, 'iterable');
39-
}
40-
41-
$context = $this->context;
42-
43-
$validator = $context->getValidator()->inContext($context);
44-
45-
foreach ($value as $key => $element) {
46-
$validator->atPath('['.$key.']')->validate($element, $constraint->constraints);
47-
}
48-
}
4924
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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\Constraints;
13+
14+
/**
15+
* @Annotation
16+
*
17+
* @author Marc Morera Merino <yuhu@mmoreram.com>
18+
* @author Marc Morales Valldepérez <marcmorales83@gmail.com>
19+
*/
20+
class Each extends AbstractComposite
21+
{
22+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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\Constraints;
13+
14+
use Symfony\Component\Validator\Constraint;
15+
use Symfony\Component\Validator\ConstraintValidator;
16+
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
17+
18+
/**
19+
* @author Marc Morera Merino <yuhu@mmoreram.com>
20+
* @author Marc Morales Valldepérez <marcmorales83@gmail.com>
21+
*/
22+
class EachValidator extends ConstraintValidator
23+
{
24+
/**
25+
* {@inheritdoc}
26+
*/
27+
public function validate($value, Constraint $constraint)
28+
{
29+
if (null === $value) {
30+
return;
31+
}
32+
33+
if (!\is_array($value) && !$value instanceof \Traversable) {
34+
throw new UnexpectedTypeException($value, 'array or Traversable');
35+
}
36+
37+
$group = $this->context->getGroup();
38+
39+
foreach ($value as $key => $element) {
40+
foreach ($constraint->constraints as $constr) {
41+
$this->context->validateValue($element, $constr, '['.$key.']', $group);
42+
}
43+
}
44+
}
45+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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\Constraints;
13+
14+
/**
15+
* @Annotation
16+
*
17+
* @author Marc Morera Merino <yuhu@mmoreram.com>
18+
* @author Marc Morales Valldepérez <marcmorales83@gmail.com>
19+
*/
20+
class None extends AbstractComposite
21+
{
22+
/**
23+
* @var string
24+
*
25+
* Message for notice Violation
26+
*/
27+
public $message = 'None of this collection should pass validation.';
28+
}
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\Constraints;
13+
14+
use Symfony\Component\Validator\Constraint;
15+
use Symfony\Component\Validator\ConstraintValidator;
16+
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
17+
18+
/**
19+
* @author Marc Morera Merino <yuhu@mmoreram.com>
20+
* @author Marc Morales Valldepérez <marcmorales83@gmail.com>
21+
*/
22+
class NoneValidator extends ConstraintValidator
23+
{
24+
/**
25+
* {@inheritdoc}
26+
*/
27+
public function validate($value, Constraint $constraint)
28+
{
29+
if (null === $value) {
30+
return;
31+
}
32+
33+
if (!\is_array($value) && !$value instanceof \Traversable) {
34+
throw new UnexpectedTypeException($value, 'array or Traversable');
35+
}
36+
37+
$group = $this->context->getGroup();
38+
39+
$totalIterations = \count($value) * \count($constraint->constraints);
40+
41+
foreach ($value as $key => $element) {
42+
foreach ($constraint->constraints as $constr) {
43+
$this->context->validateValue($element, $constr, '['.$key.']', $group);
44+
}
45+
}
46+
47+
$constraintsSuccess = $totalIterations - (int) $this->context->getViolations()->count();
48+
49+
//We clear all violations as just current Validator should add real Violations
50+
$this->context->clearViolations();
51+
52+
if ($constraintsSuccess > 0) {
53+
$this->context->addViolation($constraint->message);
54+
}
55+
}
56+
}

0 commit comments

Comments
 (0)