4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Validator\Tests\Validator;
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\Validator\Constraints\Callback;
16 use Symfony\Component\Validator\Constraints\GroupSequence;
17 use Symfony\Component\Validator\Constraints\Valid;
18 use Symfony\Component\Validator\ConstraintViolationInterface;
19 use Symfony\Component\Validator\Context\ExecutionContextInterface;
20 use Symfony\Component\Validator\Mapping\ClassMetadata;
21 use Symfony\Component\Validator\Tests\Fixtures\Entity;
22 use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory;
23 use Symfony\Component\Validator\Tests\Fixtures\GroupSequenceProviderEntity;
24 use Symfony\Component\Validator\Tests\Fixtures\Reference;
27 * @author Bernhard Schussek <bschussek@gmail.com>
29 abstract class AbstractValidatorTest extends TestCase
31 const ENTITY_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\Entity';
33 const REFERENCE_CLASS = 'Symfony\Component\Validator\Tests\Fixtures\Reference';
36 * @var FakeMetadataFactory
38 public $metadataFactory;
48 public $referenceMetadata;
50 protected function setUp()
52 $this->metadataFactory = new FakeMetadataFactory();
53 $this->metadata = new ClassMetadata(self::ENTITY_CLASS);
54 $this->referenceMetadata = new ClassMetadata(self::REFERENCE_CLASS);
55 $this->metadataFactory->addMetadata($this->metadata);
56 $this->metadataFactory->addMetadata($this->referenceMetadata);
59 protected function tearDown()
61 $this->metadataFactory = null;
62 $this->metadata = null;
63 $this->referenceMetadata = null;
66 abstract protected function validate($value, $constraints = null, $groups = null);
68 abstract protected function validateProperty($object, $propertyName, $groups = null);
70 abstract protected function validatePropertyValue($object, $propertyName, $value, $groups = null);
72 public function testValidate()
74 $callback = function ($value, ExecutionContextInterface $context) {
75 $this->assertNull($context->getClassName());
76 $this->assertNull($context->getPropertyName());
77 $this->assertSame('', $context->getPropertyPath());
78 $this->assertSame('Group', $context->getGroup());
79 $this->assertSame('Bernhard', $context->getRoot());
80 $this->assertSame('Bernhard', $context->getValue());
81 $this->assertSame('Bernhard', $value);
83 $context->addViolation('Message %param%', array('%param%' => 'value'));
86 $constraint = new Callback(array(
87 'callback' => $callback,
91 $violations = $this->validate('Bernhard', $constraint, 'Group');
93 /* @var ConstraintViolationInterface[] $violations */
94 $this->assertCount(1, $violations);
95 $this->assertSame('Message value', $violations[0]->getMessage());
96 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
97 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
98 $this->assertSame('', $violations[0]->getPropertyPath());
99 $this->assertSame('Bernhard', $violations[0]->getRoot());
100 $this->assertSame('Bernhard', $violations[0]->getInvalidValue());
101 $this->assertNull($violations[0]->getPlural());
102 $this->assertNull($violations[0]->getCode());
105 public function testClassConstraint()
107 $entity = new Entity();
109 $callback = function ($value, ExecutionContextInterface $context) use ($entity) {
110 $this->assertSame($this::ENTITY_CLASS, $context->getClassName());
111 $this->assertNull($context->getPropertyName());
112 $this->assertSame('', $context->getPropertyPath());
113 $this->assertSame('Group', $context->getGroup());
114 $this->assertSame($this->metadata, $context->getMetadata());
115 $this->assertSame($entity, $context->getRoot());
116 $this->assertSame($entity, $context->getValue());
117 $this->assertSame($entity, $value);
119 $context->addViolation('Message %param%', array('%param%' => 'value'));
122 $this->metadata->addConstraint(new Callback(array(
123 'callback' => $callback,
127 $violations = $this->validate($entity, null, 'Group');
129 /* @var ConstraintViolationInterface[] $violations */
130 $this->assertCount(1, $violations);
131 $this->assertSame('Message value', $violations[0]->getMessage());
132 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
133 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
134 $this->assertSame('', $violations[0]->getPropertyPath());
135 $this->assertSame($entity, $violations[0]->getRoot());
136 $this->assertSame($entity, $violations[0]->getInvalidValue());
137 $this->assertNull($violations[0]->getPlural());
138 $this->assertNull($violations[0]->getCode());
141 public function testPropertyConstraint()
143 $entity = new Entity();
144 $entity->firstName = 'Bernhard';
146 $callback = function ($value, ExecutionContextInterface $context) use ($entity) {
147 $propertyMetadatas = $this->metadata->getPropertyMetadata('firstName');
149 $this->assertSame($this::ENTITY_CLASS, $context->getClassName());
150 $this->assertSame('firstName', $context->getPropertyName());
151 $this->assertSame('firstName', $context->getPropertyPath());
152 $this->assertSame('Group', $context->getGroup());
153 $this->assertSame($propertyMetadatas[0], $context->getMetadata());
154 $this->assertSame($entity, $context->getRoot());
155 $this->assertSame('Bernhard', $context->getValue());
156 $this->assertSame('Bernhard', $value);
158 $context->addViolation('Message %param%', array('%param%' => 'value'));
161 $this->metadata->addPropertyConstraint('firstName', new Callback(array(
162 'callback' => $callback,
166 $violations = $this->validate($entity, null, 'Group');
168 /* @var ConstraintViolationInterface[] $violations */
169 $this->assertCount(1, $violations);
170 $this->assertSame('Message value', $violations[0]->getMessage());
171 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
172 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
173 $this->assertSame('firstName', $violations[0]->getPropertyPath());
174 $this->assertSame($entity, $violations[0]->getRoot());
175 $this->assertSame('Bernhard', $violations[0]->getInvalidValue());
176 $this->assertNull($violations[0]->getPlural());
177 $this->assertNull($violations[0]->getCode());
180 public function testGetterConstraint()
182 $entity = new Entity();
183 $entity->setLastName('Schussek');
185 $callback = function ($value, ExecutionContextInterface $context) use ($entity) {
186 $propertyMetadatas = $this->metadata->getPropertyMetadata('lastName');
188 $this->assertSame($this::ENTITY_CLASS, $context->getClassName());
189 $this->assertSame('lastName', $context->getPropertyName());
190 $this->assertSame('lastName', $context->getPropertyPath());
191 $this->assertSame('Group', $context->getGroup());
192 $this->assertSame($propertyMetadatas[0], $context->getMetadata());
193 $this->assertSame($entity, $context->getRoot());
194 $this->assertSame('Schussek', $context->getValue());
195 $this->assertSame('Schussek', $value);
197 $context->addViolation('Message %param%', array('%param%' => 'value'));
200 $this->metadata->addGetterConstraint('lastName', new Callback(array(
201 'callback' => $callback,
205 $violations = $this->validate($entity, null, 'Group');
207 /* @var ConstraintViolationInterface[] $violations */
208 $this->assertCount(1, $violations);
209 $this->assertSame('Message value', $violations[0]->getMessage());
210 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
211 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
212 $this->assertSame('lastName', $violations[0]->getPropertyPath());
213 $this->assertSame($entity, $violations[0]->getRoot());
214 $this->assertSame('Schussek', $violations[0]->getInvalidValue());
215 $this->assertNull($violations[0]->getPlural());
216 $this->assertNull($violations[0]->getCode());
219 public function testArray()
221 $entity = new Entity();
222 $array = array('key' => $entity);
224 $callback = function ($value, ExecutionContextInterface $context) use ($entity, $array) {
225 $this->assertSame($this::ENTITY_CLASS, $context->getClassName());
226 $this->assertNull($context->getPropertyName());
227 $this->assertSame('[key]', $context->getPropertyPath());
228 $this->assertSame('Group', $context->getGroup());
229 $this->assertSame($this->metadata, $context->getMetadata());
230 $this->assertSame($array, $context->getRoot());
231 $this->assertSame($entity, $context->getValue());
232 $this->assertSame($entity, $value);
234 $context->addViolation('Message %param%', array('%param%' => 'value'));
237 $this->metadata->addConstraint(new Callback(array(
238 'callback' => $callback,
242 $violations = $this->validate($array, null, 'Group');
244 /* @var ConstraintViolationInterface[] $violations */
245 $this->assertCount(1, $violations);
246 $this->assertSame('Message value', $violations[0]->getMessage());
247 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
248 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
249 $this->assertSame('[key]', $violations[0]->getPropertyPath());
250 $this->assertSame($array, $violations[0]->getRoot());
251 $this->assertSame($entity, $violations[0]->getInvalidValue());
252 $this->assertNull($violations[0]->getPlural());
253 $this->assertNull($violations[0]->getCode());
256 public function testRecursiveArray()
258 $entity = new Entity();
259 $array = array(2 => array('key' => $entity));
261 $callback = function ($value, ExecutionContextInterface $context) use ($entity, $array) {
262 $this->assertSame($this::ENTITY_CLASS, $context->getClassName());
263 $this->assertNull($context->getPropertyName());
264 $this->assertSame('[2][key]', $context->getPropertyPath());
265 $this->assertSame('Group', $context->getGroup());
266 $this->assertSame($this->metadata, $context->getMetadata());
267 $this->assertSame($array, $context->getRoot());
268 $this->assertSame($entity, $context->getValue());
269 $this->assertSame($entity, $value);
271 $context->addViolation('Message %param%', array('%param%' => 'value'));
274 $this->metadata->addConstraint(new Callback(array(
275 'callback' => $callback,
279 $violations = $this->validate($array, null, 'Group');
281 /* @var ConstraintViolationInterface[] $violations */
282 $this->assertCount(1, $violations);
283 $this->assertSame('Message value', $violations[0]->getMessage());
284 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
285 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
286 $this->assertSame('[2][key]', $violations[0]->getPropertyPath());
287 $this->assertSame($array, $violations[0]->getRoot());
288 $this->assertSame($entity, $violations[0]->getInvalidValue());
289 $this->assertNull($violations[0]->getPlural());
290 $this->assertNull($violations[0]->getCode());
293 public function testTraversable()
295 $entity = new Entity();
296 $traversable = new \ArrayIterator(array('key' => $entity));
298 $callback = function ($value, ExecutionContextInterface $context) use ($entity, $traversable) {
299 $this->assertSame($this::ENTITY_CLASS, $context->getClassName());
300 $this->assertNull($context->getPropertyName());
301 $this->assertSame('[key]', $context->getPropertyPath());
302 $this->assertSame('Group', $context->getGroup());
303 $this->assertSame($this->metadata, $context->getMetadata());
304 $this->assertSame($traversable, $context->getRoot());
305 $this->assertSame($entity, $context->getValue());
306 $this->assertSame($entity, $value);
308 $context->addViolation('Message %param%', array('%param%' => 'value'));
311 $this->metadata->addConstraint(new Callback(array(
312 'callback' => $callback,
316 $violations = $this->validate($traversable, null, 'Group');
318 /* @var ConstraintViolationInterface[] $violations */
319 $this->assertCount(1, $violations);
320 $this->assertSame('Message value', $violations[0]->getMessage());
321 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
322 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
323 $this->assertSame('[key]', $violations[0]->getPropertyPath());
324 $this->assertSame($traversable, $violations[0]->getRoot());
325 $this->assertSame($entity, $violations[0]->getInvalidValue());
326 $this->assertNull($violations[0]->getPlural());
327 $this->assertNull($violations[0]->getCode());
330 public function testRecursiveTraversable()
332 $entity = new Entity();
333 $traversable = new \ArrayIterator(array(
334 2 => new \ArrayIterator(array('key' => $entity)),
337 $callback = function ($value, ExecutionContextInterface $context) use ($entity, $traversable) {
338 $this->assertSame($this::ENTITY_CLASS, $context->getClassName());
339 $this->assertNull($context->getPropertyName());
340 $this->assertSame('[2][key]', $context->getPropertyPath());
341 $this->assertSame('Group', $context->getGroup());
342 $this->assertSame($this->metadata, $context->getMetadata());
343 $this->assertSame($traversable, $context->getRoot());
344 $this->assertSame($entity, $context->getValue());
345 $this->assertSame($entity, $value);
347 $context->addViolation('Message %param%', array('%param%' => 'value'));
350 $this->metadata->addConstraint(new Callback(array(
351 'callback' => $callback,
355 $violations = $this->validate($traversable, null, 'Group');
357 /* @var ConstraintViolationInterface[] $violations */
358 $this->assertCount(1, $violations);
359 $this->assertSame('Message value', $violations[0]->getMessage());
360 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
361 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
362 $this->assertSame('[2][key]', $violations[0]->getPropertyPath());
363 $this->assertSame($traversable, $violations[0]->getRoot());
364 $this->assertSame($entity, $violations[0]->getInvalidValue());
365 $this->assertNull($violations[0]->getPlural());
366 $this->assertNull($violations[0]->getCode());
369 public function testReferenceClassConstraint()
371 $entity = new Entity();
372 $entity->reference = new Reference();
374 $callback = function ($value, ExecutionContextInterface $context) use ($entity) {
375 $this->assertSame($this::REFERENCE_CLASS, $context->getClassName());
376 $this->assertNull($context->getPropertyName());
377 $this->assertSame('reference', $context->getPropertyPath());
378 $this->assertSame('Group', $context->getGroup());
379 $this->assertSame($this->referenceMetadata, $context->getMetadata());
380 $this->assertSame($entity, $context->getRoot());
381 $this->assertSame($entity->reference, $context->getValue());
382 $this->assertSame($entity->reference, $value);
384 $context->addViolation('Message %param%', array('%param%' => 'value'));
387 $this->metadata->addPropertyConstraint('reference', new Valid());
388 $this->referenceMetadata->addConstraint(new Callback(array(
389 'callback' => $callback,
393 $violations = $this->validate($entity, null, 'Group');
395 /* @var ConstraintViolationInterface[] $violations */
396 $this->assertCount(1, $violations);
397 $this->assertSame('Message value', $violations[0]->getMessage());
398 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
399 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
400 $this->assertSame('reference', $violations[0]->getPropertyPath());
401 $this->assertSame($entity, $violations[0]->getRoot());
402 $this->assertSame($entity->reference, $violations[0]->getInvalidValue());
403 $this->assertNull($violations[0]->getPlural());
404 $this->assertNull($violations[0]->getCode());
407 public function testReferencePropertyConstraint()
409 $entity = new Entity();
410 $entity->reference = new Reference();
411 $entity->reference->value = 'Foobar';
413 $callback = function ($value, ExecutionContextInterface $context) use ($entity) {
414 $propertyMetadatas = $this->referenceMetadata->getPropertyMetadata('value');
416 $this->assertSame($this::REFERENCE_CLASS, $context->getClassName());
417 $this->assertSame('value', $context->getPropertyName());
418 $this->assertSame('reference.value', $context->getPropertyPath());
419 $this->assertSame('Group', $context->getGroup());
420 $this->assertSame($propertyMetadatas[0], $context->getMetadata());
421 $this->assertSame($entity, $context->getRoot());
422 $this->assertSame('Foobar', $context->getValue());
423 $this->assertSame('Foobar', $value);
425 $context->addViolation('Message %param%', array('%param%' => 'value'));
428 $this->metadata->addPropertyConstraint('reference', new Valid());
429 $this->referenceMetadata->addPropertyConstraint('value', new Callback(array(
430 'callback' => $callback,
434 $violations = $this->validate($entity, null, 'Group');
436 /* @var ConstraintViolationInterface[] $violations */
437 $this->assertCount(1, $violations);
438 $this->assertSame('Message value', $violations[0]->getMessage());
439 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
440 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
441 $this->assertSame('reference.value', $violations[0]->getPropertyPath());
442 $this->assertSame($entity, $violations[0]->getRoot());
443 $this->assertSame('Foobar', $violations[0]->getInvalidValue());
444 $this->assertNull($violations[0]->getPlural());
445 $this->assertNull($violations[0]->getCode());
448 public function testReferenceGetterConstraint()
450 $entity = new Entity();
451 $entity->reference = new Reference();
452 $entity->reference->setPrivateValue('Bamboo');
454 $callback = function ($value, ExecutionContextInterface $context) use ($entity) {
455 $propertyMetadatas = $this->referenceMetadata->getPropertyMetadata('privateValue');
457 $this->assertSame($this::REFERENCE_CLASS, $context->getClassName());
458 $this->assertSame('privateValue', $context->getPropertyName());
459 $this->assertSame('reference.privateValue', $context->getPropertyPath());
460 $this->assertSame('Group', $context->getGroup());
461 $this->assertSame($propertyMetadatas[0], $context->getMetadata());
462 $this->assertSame($entity, $context->getRoot());
463 $this->assertSame('Bamboo', $context->getValue());
464 $this->assertSame('Bamboo', $value);
466 $context->addViolation('Message %param%', array('%param%' => 'value'));
469 $this->metadata->addPropertyConstraint('reference', new Valid());
470 $this->referenceMetadata->addPropertyConstraint('privateValue', new Callback(array(
471 'callback' => $callback,
475 $violations = $this->validate($entity, null, 'Group');
477 /* @var ConstraintViolationInterface[] $violations */
478 $this->assertCount(1, $violations);
479 $this->assertSame('Message value', $violations[0]->getMessage());
480 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
481 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
482 $this->assertSame('reference.privateValue', $violations[0]->getPropertyPath());
483 $this->assertSame($entity, $violations[0]->getRoot());
484 $this->assertSame('Bamboo', $violations[0]->getInvalidValue());
485 $this->assertNull($violations[0]->getPlural());
486 $this->assertNull($violations[0]->getCode());
489 public function testsIgnoreNullReference()
491 $entity = new Entity();
492 $entity->reference = null;
494 $this->metadata->addPropertyConstraint('reference', new Valid());
496 $violations = $this->validate($entity);
498 /* @var ConstraintViolationInterface[] $violations */
499 $this->assertCount(0, $violations);
503 * @expectedException \Symfony\Component\Validator\Exception\NoSuchMetadataException
505 public function testFailOnScalarReferences()
507 $entity = new Entity();
508 $entity->reference = 'string';
510 $this->metadata->addPropertyConstraint('reference', new Valid());
512 $this->validate($entity);
515 public function testArrayReference()
517 $entity = new Entity();
518 $entity->reference = array('key' => new Reference());
520 $callback = function ($value, ExecutionContextInterface $context) use ($entity) {
521 $this->assertSame($this::REFERENCE_CLASS, $context->getClassName());
522 $this->assertNull($context->getPropertyName());
523 $this->assertSame('reference[key]', $context->getPropertyPath());
524 $this->assertSame('Group', $context->getGroup());
525 $this->assertSame($this->referenceMetadata, $context->getMetadata());
526 $this->assertSame($entity, $context->getRoot());
527 $this->assertSame($entity->reference['key'], $context->getValue());
528 $this->assertSame($entity->reference['key'], $value);
530 $context->addViolation('Message %param%', array('%param%' => 'value'));
533 $this->metadata->addPropertyConstraint('reference', new Valid());
534 $this->referenceMetadata->addConstraint(new Callback(array(
535 'callback' => $callback,
539 $violations = $this->validate($entity, null, 'Group');
541 /* @var ConstraintViolationInterface[] $violations */
542 $this->assertCount(1, $violations);
543 $this->assertSame('Message value', $violations[0]->getMessage());
544 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
545 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
546 $this->assertSame('reference[key]', $violations[0]->getPropertyPath());
547 $this->assertSame($entity, $violations[0]->getRoot());
548 $this->assertSame($entity->reference['key'], $violations[0]->getInvalidValue());
549 $this->assertNull($violations[0]->getPlural());
550 $this->assertNull($violations[0]->getCode());
553 // https://github.com/symfony/symfony/issues/6246
554 public function testRecursiveArrayReference()
556 $entity = new Entity();
557 $entity->reference = array(2 => array('key' => new Reference()));
559 $callback = function ($value, ExecutionContextInterface $context) use ($entity) {
560 $this->assertSame($this::REFERENCE_CLASS, $context->getClassName());
561 $this->assertNull($context->getPropertyName());
562 $this->assertSame('reference[2][key]', $context->getPropertyPath());
563 $this->assertSame('Group', $context->getGroup());
564 $this->assertSame($this->referenceMetadata, $context->getMetadata());
565 $this->assertSame($entity, $context->getRoot());
566 $this->assertSame($entity->reference[2]['key'], $context->getValue());
567 $this->assertSame($entity->reference[2]['key'], $value);
569 $context->addViolation('Message %param%', array('%param%' => 'value'));
572 $this->metadata->addPropertyConstraint('reference', new Valid());
573 $this->referenceMetadata->addConstraint(new Callback(array(
574 'callback' => $callback,
578 $violations = $this->validate($entity, null, 'Group');
580 /* @var ConstraintViolationInterface[] $violations */
581 $this->assertCount(1, $violations);
582 $this->assertSame('Message value', $violations[0]->getMessage());
583 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
584 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
585 $this->assertSame('reference[2][key]', $violations[0]->getPropertyPath());
586 $this->assertSame($entity, $violations[0]->getRoot());
587 $this->assertSame($entity->reference[2]['key'], $violations[0]->getInvalidValue());
588 $this->assertNull($violations[0]->getPlural());
589 $this->assertNull($violations[0]->getCode());
592 public function testArrayTraversalCannotBeDisabled()
594 $entity = new Entity();
595 $entity->reference = array('key' => new Reference());
597 $callback = function ($value, ExecutionContextInterface $context) {
598 $context->addViolation('Message %param%', array('%param%' => 'value'));
601 $this->metadata->addPropertyConstraint('reference', new Valid(array(
604 $this->referenceMetadata->addConstraint(new Callback($callback));
606 $violations = $this->validate($entity);
608 /* @var ConstraintViolationInterface[] $violations */
609 $this->assertCount(1, $violations);
612 public function testRecursiveArrayTraversalCannotBeDisabled()
614 $entity = new Entity();
615 $entity->reference = array(2 => array('key' => new Reference()));
617 $callback = function ($value, ExecutionContextInterface $context) {
618 $context->addViolation('Message %param%', array('%param%' => 'value'));
621 $this->metadata->addPropertyConstraint('reference', new Valid(array(
624 $this->referenceMetadata->addConstraint(new Callback($callback));
626 $violations = $this->validate($entity);
628 /* @var ConstraintViolationInterface[] $violations */
629 $this->assertCount(1, $violations);
632 public function testIgnoreScalarsDuringArrayTraversal()
634 $entity = new Entity();
635 $entity->reference = array('string', 1234);
637 $this->metadata->addPropertyConstraint('reference', new Valid());
639 $violations = $this->validate($entity);
641 /* @var ConstraintViolationInterface[] $violations */
642 $this->assertCount(0, $violations);
645 public function testIgnoreNullDuringArrayTraversal()
647 $entity = new Entity();
648 $entity->reference = array(null);
650 $this->metadata->addPropertyConstraint('reference', new Valid());
652 $violations = $this->validate($entity);
654 /* @var ConstraintViolationInterface[] $violations */
655 $this->assertCount(0, $violations);
658 public function testTraversableReference()
660 $entity = new Entity();
661 $entity->reference = new \ArrayIterator(array('key' => new Reference()));
663 $callback = function ($value, ExecutionContextInterface $context) use ($entity) {
664 $this->assertSame($this::REFERENCE_CLASS, $context->getClassName());
665 $this->assertNull($context->getPropertyName());
666 $this->assertSame('reference[key]', $context->getPropertyPath());
667 $this->assertSame('Group', $context->getGroup());
668 $this->assertSame($this->referenceMetadata, $context->getMetadata());
669 $this->assertSame($entity, $context->getRoot());
670 $this->assertSame($entity->reference['key'], $context->getValue());
671 $this->assertSame($entity->reference['key'], $value);
673 $context->addViolation('Message %param%', array('%param%' => 'value'));
676 $this->metadata->addPropertyConstraint('reference', new Valid());
677 $this->referenceMetadata->addConstraint(new Callback(array(
678 'callback' => $callback,
682 $violations = $this->validate($entity, null, 'Group');
684 /* @var ConstraintViolationInterface[] $violations */
685 $this->assertCount(1, $violations);
686 $this->assertSame('Message value', $violations[0]->getMessage());
687 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
688 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
689 $this->assertSame('reference[key]', $violations[0]->getPropertyPath());
690 $this->assertSame($entity, $violations[0]->getRoot());
691 $this->assertSame($entity->reference['key'], $violations[0]->getInvalidValue());
692 $this->assertNull($violations[0]->getPlural());
693 $this->assertNull($violations[0]->getCode());
696 public function testDisableTraversableTraversal()
698 $entity = new Entity();
699 $entity->reference = new \ArrayIterator(array('key' => new Reference()));
701 $callback = function ($value, ExecutionContextInterface $context) {
702 $context->addViolation('Message %param%', array('%param%' => 'value'));
705 $this->metadataFactory->addMetadata(new ClassMetadata('ArrayIterator'));
706 $this->metadata->addPropertyConstraint('reference', new Valid(array(
709 $this->referenceMetadata->addConstraint(new Callback($callback));
711 $violations = $this->validate($entity);
713 /* @var ConstraintViolationInterface[] $violations */
714 $this->assertCount(0, $violations);
718 * @expectedException \Symfony\Component\Validator\Exception\NoSuchMetadataException
720 public function testMetadataMustExistIfTraversalIsDisabled()
722 $entity = new Entity();
723 $entity->reference = new \ArrayIterator();
725 $this->metadata->addPropertyConstraint('reference', new Valid(array(
729 $this->validate($entity);
732 public function testEnableRecursiveTraversableTraversal()
734 $entity = new Entity();
735 $entity->reference = new \ArrayIterator(array(
736 2 => new \ArrayIterator(array('key' => new Reference())),
739 $callback = function ($value, ExecutionContextInterface $context) use ($entity) {
740 $this->assertSame($this::REFERENCE_CLASS, $context->getClassName());
741 $this->assertNull($context->getPropertyName());
742 $this->assertSame('reference[2][key]', $context->getPropertyPath());
743 $this->assertSame('Group', $context->getGroup());
744 $this->assertSame($this->referenceMetadata, $context->getMetadata());
745 $this->assertSame($entity, $context->getRoot());
746 $this->assertSame($entity->reference[2]['key'], $context->getValue());
747 $this->assertSame($entity->reference[2]['key'], $value);
749 $context->addViolation('Message %param%', array('%param%' => 'value'));
752 $this->metadata->addPropertyConstraint('reference', new Valid(array(
755 $this->referenceMetadata->addConstraint(new Callback(array(
756 'callback' => $callback,
760 $violations = $this->validate($entity, null, 'Group');
762 /* @var ConstraintViolationInterface[] $violations */
763 $this->assertCount(1, $violations);
764 $this->assertSame('Message value', $violations[0]->getMessage());
765 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
766 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
767 $this->assertSame('reference[2][key]', $violations[0]->getPropertyPath());
768 $this->assertSame($entity, $violations[0]->getRoot());
769 $this->assertSame($entity->reference[2]['key'], $violations[0]->getInvalidValue());
770 $this->assertNull($violations[0]->getPlural());
771 $this->assertNull($violations[0]->getCode());
774 public function testValidateProperty()
776 $entity = new Entity();
777 $entity->firstName = 'Bernhard';
778 $entity->setLastName('Schussek');
780 $callback1 = function ($value, ExecutionContextInterface $context) use ($entity) {
781 $propertyMetadatas = $this->metadata->getPropertyMetadata('firstName');
783 $this->assertSame($this::ENTITY_CLASS, $context->getClassName());
784 $this->assertSame('firstName', $context->getPropertyName());
785 $this->assertSame('firstName', $context->getPropertyPath());
786 $this->assertSame('Group', $context->getGroup());
787 $this->assertSame($propertyMetadatas[0], $context->getMetadata());
788 $this->assertSame($entity, $context->getRoot());
789 $this->assertSame('Bernhard', $context->getValue());
790 $this->assertSame('Bernhard', $value);
792 $context->addViolation('Message %param%', array('%param%' => 'value'));
795 $callback2 = function ($value, ExecutionContextInterface $context) {
796 $context->addViolation('Other violation');
799 $this->metadata->addPropertyConstraint('firstName', new Callback(array(
800 'callback' => $callback1,
803 $this->metadata->addPropertyConstraint('lastName', new Callback(array(
804 'callback' => $callback2,
808 $violations = $this->validateProperty($entity, 'firstName', 'Group');
810 /* @var ConstraintViolationInterface[] $violations */
811 $this->assertCount(1, $violations);
812 $this->assertSame('Message value', $violations[0]->getMessage());
813 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
814 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
815 $this->assertSame('firstName', $violations[0]->getPropertyPath());
816 $this->assertSame($entity, $violations[0]->getRoot());
817 $this->assertSame('Bernhard', $violations[0]->getInvalidValue());
818 $this->assertNull($violations[0]->getPlural());
819 $this->assertNull($violations[0]->getCode());
823 * https://github.com/symfony/symfony/issues/11604.
825 public function testValidatePropertyWithoutConstraints()
827 $entity = new Entity();
828 $violations = $this->validateProperty($entity, 'lastName');
830 $this->assertCount(0, $violations, '->validateProperty() returns no violations if no constraints have been configured for the property being validated');
833 public function testValidatePropertyValue()
835 $entity = new Entity();
836 $entity->setLastName('Schussek');
838 $callback1 = function ($value, ExecutionContextInterface $context) use ($entity) {
839 $propertyMetadatas = $this->metadata->getPropertyMetadata('firstName');
841 $this->assertSame($this::ENTITY_CLASS, $context->getClassName());
842 $this->assertSame('firstName', $context->getPropertyName());
843 $this->assertSame('firstName', $context->getPropertyPath());
844 $this->assertSame('Group', $context->getGroup());
845 $this->assertSame($propertyMetadatas[0], $context->getMetadata());
846 $this->assertSame($entity, $context->getRoot());
847 $this->assertSame('Bernhard', $context->getValue());
848 $this->assertSame('Bernhard', $value);
850 $context->addViolation('Message %param%', array('%param%' => 'value'));
853 $callback2 = function ($value, ExecutionContextInterface $context) {
854 $context->addViolation('Other violation');
857 $this->metadata->addPropertyConstraint('firstName', new Callback(array(
858 'callback' => $callback1,
861 $this->metadata->addPropertyConstraint('lastName', new Callback(array(
862 'callback' => $callback2,
866 $violations = $this->validatePropertyValue(
873 /* @var ConstraintViolationInterface[] $violations */
874 $this->assertCount(1, $violations);
875 $this->assertSame('Message value', $violations[0]->getMessage());
876 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
877 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
878 $this->assertSame('firstName', $violations[0]->getPropertyPath());
879 $this->assertSame($entity, $violations[0]->getRoot());
880 $this->assertSame('Bernhard', $violations[0]->getInvalidValue());
881 $this->assertNull($violations[0]->getPlural());
882 $this->assertNull($violations[0]->getCode());
885 public function testValidatePropertyValueWithClassName()
887 $callback1 = function ($value, ExecutionContextInterface $context) {
888 $propertyMetadatas = $this->metadata->getPropertyMetadata('firstName');
890 $this->assertSame($this::ENTITY_CLASS, $context->getClassName());
891 $this->assertSame('firstName', $context->getPropertyName());
892 $this->assertSame('', $context->getPropertyPath());
893 $this->assertSame('Group', $context->getGroup());
894 $this->assertSame($propertyMetadatas[0], $context->getMetadata());
895 $this->assertSame('Bernhard', $context->getRoot());
896 $this->assertSame('Bernhard', $context->getValue());
897 $this->assertSame('Bernhard', $value);
899 $context->addViolation('Message %param%', array('%param%' => 'value'));
902 $callback2 = function ($value, ExecutionContextInterface $context) {
903 $context->addViolation('Other violation');
906 $this->metadata->addPropertyConstraint('firstName', new Callback(array(
907 'callback' => $callback1,
910 $this->metadata->addPropertyConstraint('lastName', new Callback(array(
911 'callback' => $callback2,
915 $violations = $this->validatePropertyValue(
922 /* @var ConstraintViolationInterface[] $violations */
923 $this->assertCount(1, $violations);
924 $this->assertSame('Message value', $violations[0]->getMessage());
925 $this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
926 $this->assertSame(array('%param%' => 'value'), $violations[0]->getParameters());
927 $this->assertSame('', $violations[0]->getPropertyPath());
928 $this->assertSame('Bernhard', $violations[0]->getRoot());
929 $this->assertSame('Bernhard', $violations[0]->getInvalidValue());
930 $this->assertNull($violations[0]->getPlural());
931 $this->assertNull($violations[0]->getCode());
935 * https://github.com/symfony/symfony/issues/11604.
937 public function testValidatePropertyValueWithoutConstraints()
939 $entity = new Entity();
940 $violations = $this->validatePropertyValue($entity, 'lastName', 'foo');
942 $this->assertCount(0, $violations, '->validatePropertyValue() returns no violations if no constraints have been configured for the property being validated');
945 public function testValidateObjectOnlyOncePerGroup()
947 $entity = new Entity();
948 $entity->reference = new Reference();
949 $entity->reference2 = $entity->reference;
951 $callback = function ($value, ExecutionContextInterface $context) {
952 $context->addViolation('Message');
955 $this->metadata->addPropertyConstraint('reference', new Valid());
956 $this->metadata->addPropertyConstraint('reference2', new Valid());
957 $this->referenceMetadata->addConstraint(new Callback($callback));
959 $violations = $this->validate($entity);
961 /* @var ConstraintViolationInterface[] $violations */
962 $this->assertCount(1, $violations);
965 public function testValidateDifferentObjectsSeparately()
967 $entity = new Entity();
968 $entity->reference = new Reference();
969 $entity->reference2 = new Reference();
971 $callback = function ($value, ExecutionContextInterface $context) {
972 $context->addViolation('Message');
975 $this->metadata->addPropertyConstraint('reference', new Valid());
976 $this->metadata->addPropertyConstraint('reference2', new Valid());
977 $this->referenceMetadata->addConstraint(new Callback($callback));
979 $violations = $this->validate($entity);
981 /* @var ConstraintViolationInterface[] $violations */
982 $this->assertCount(2, $violations);
985 public function testValidateSingleGroup()
987 $entity = new Entity();
989 $callback = function ($value, ExecutionContextInterface $context) {
990 $context->addViolation('Message');
993 $this->metadata->addConstraint(new Callback(array(
994 'callback' => $callback,
995 'groups' => 'Group 1',
997 $this->metadata->addConstraint(new Callback(array(
998 'callback' => $callback,
999 'groups' => 'Group 2',
1002 $violations = $this->validate($entity, null, 'Group 2');
1004 /* @var ConstraintViolationInterface[] $violations */
1005 $this->assertCount(1, $violations);
1008 public function testValidateMultipleGroups()
1010 $entity = new Entity();
1012 $callback = function ($value, ExecutionContextInterface $context) {
1013 $context->addViolation('Message');
1016 $this->metadata->addConstraint(new Callback(array(
1017 'callback' => $callback,
1018 'groups' => 'Group 1',
1020 $this->metadata->addConstraint(new Callback(array(
1021 'callback' => $callback,
1022 'groups' => 'Group 2',
1025 $violations = $this->validate($entity, null, array('Group 1', 'Group 2'));
1027 /* @var ConstraintViolationInterface[] $violations */
1028 $this->assertCount(2, $violations);
1031 public function testReplaceDefaultGroupByGroupSequenceObject()
1033 $entity = new Entity();
1035 $callback1 = function ($value, ExecutionContextInterface $context) {
1036 $context->addViolation('Violation in Group 2');
1038 $callback2 = function ($value, ExecutionContextInterface $context) {
1039 $context->addViolation('Violation in Group 3');
1042 $this->metadata->addConstraint(new Callback(array(
1043 'callback' => function () {},
1044 'groups' => 'Group 1',
1046 $this->metadata->addConstraint(new Callback(array(
1047 'callback' => $callback1,
1048 'groups' => 'Group 2',
1050 $this->metadata->addConstraint(new Callback(array(
1051 'callback' => $callback2,
1052 'groups' => 'Group 3',
1055 $sequence = new GroupSequence(array('Group 1', 'Group 2', 'Group 3', 'Entity'));
1056 $this->metadata->setGroupSequence($sequence);
1058 $violations = $this->validate($entity, null, 'Default');
1060 /* @var ConstraintViolationInterface[] $violations */
1061 $this->assertCount(1, $violations);
1062 $this->assertSame('Violation in Group 2', $violations[0]->getMessage());
1065 public function testReplaceDefaultGroupByGroupSequenceArray()
1067 $entity = new Entity();
1069 $callback1 = function ($value, ExecutionContextInterface $context) {
1070 $context->addViolation('Violation in Group 2');
1072 $callback2 = function ($value, ExecutionContextInterface $context) {
1073 $context->addViolation('Violation in Group 3');
1076 $this->metadata->addConstraint(new Callback(array(
1077 'callback' => function () {},
1078 'groups' => 'Group 1',
1080 $this->metadata->addConstraint(new Callback(array(
1081 'callback' => $callback1,
1082 'groups' => 'Group 2',
1084 $this->metadata->addConstraint(new Callback(array(
1085 'callback' => $callback2,
1086 'groups' => 'Group 3',
1089 $sequence = array('Group 1', 'Group 2', 'Group 3', 'Entity');
1090 $this->metadata->setGroupSequence($sequence);
1092 $violations = $this->validate($entity, null, 'Default');
1094 /* @var ConstraintViolationInterface[] $violations */
1095 $this->assertCount(1, $violations);
1096 $this->assertSame('Violation in Group 2', $violations[0]->getMessage());
1099 public function testPropagateDefaultGroupToReferenceWhenReplacingDefaultGroup()
1101 $entity = new Entity();
1102 $entity->reference = new Reference();
1104 $callback1 = function ($value, ExecutionContextInterface $context) {
1105 $context->addViolation('Violation in Default group');
1107 $callback2 = function ($value, ExecutionContextInterface $context) {
1108 $context->addViolation('Violation in group sequence');
1111 $this->metadata->addPropertyConstraint('reference', new Valid());
1112 $this->referenceMetadata->addConstraint(new Callback(array(
1113 'callback' => $callback1,
1114 'groups' => 'Default',
1116 $this->referenceMetadata->addConstraint(new Callback(array(
1117 'callback' => $callback2,
1118 'groups' => 'Group 1',
1121 $sequence = new GroupSequence(array('Group 1', 'Entity'));
1122 $this->metadata->setGroupSequence($sequence);
1124 $violations = $this->validate($entity, null, 'Default');
1126 /* @var ConstraintViolationInterface[] $violations */
1127 $this->assertCount(1, $violations);
1128 $this->assertSame('Violation in Default group', $violations[0]->getMessage());
1131 public function testValidateCustomGroupWhenDefaultGroupWasReplaced()
1133 $entity = new Entity();
1135 $callback1 = function ($value, ExecutionContextInterface $context) {
1136 $context->addViolation('Violation in other group');
1138 $callback2 = function ($value, ExecutionContextInterface $context) {
1139 $context->addViolation('Violation in group sequence');
1142 $this->metadata->addConstraint(new Callback(array(
1143 'callback' => $callback1,
1144 'groups' => 'Other Group',
1146 $this->metadata->addConstraint(new Callback(array(
1147 'callback' => $callback2,
1148 'groups' => 'Group 1',
1151 $sequence = new GroupSequence(array('Group 1', 'Entity'));
1152 $this->metadata->setGroupSequence($sequence);
1154 $violations = $this->validate($entity, null, 'Other Group');
1156 /* @var ConstraintViolationInterface[] $violations */
1157 $this->assertCount(1, $violations);
1158 $this->assertSame('Violation in other group', $violations[0]->getMessage());
1162 * @dataProvider getTestReplaceDefaultGroup
1164 public function testReplaceDefaultGroup($sequence, array $assertViolations)
1166 $entity = new GroupSequenceProviderEntity($sequence);
1168 $callback1 = function ($value, ExecutionContextInterface $context) {
1169 $context->addViolation('Violation in Group 2');
1171 $callback2 = function ($value, ExecutionContextInterface $context) {
1172 $context->addViolation('Violation in Group 3');
1175 $metadata = new ClassMetadata(get_class($entity));
1176 $metadata->addConstraint(new Callback(array(
1177 'callback' => function () {},
1178 'groups' => 'Group 1',
1180 $metadata->addConstraint(new Callback(array(
1181 'callback' => $callback1,
1182 'groups' => 'Group 2',
1184 $metadata->addConstraint(new Callback(array(
1185 'callback' => $callback2,
1186 'groups' => 'Group 3',
1188 $metadata->setGroupSequenceProvider(true);
1190 $this->metadataFactory->addMetadata($metadata);
1192 $violations = $this->validate($entity, null, 'Default');
1194 /* @var ConstraintViolationInterface[] $violations */
1195 $this->assertCount(count($assertViolations), $violations);
1196 foreach ($assertViolations as $key => $message) {
1197 $this->assertSame($message, $violations[$key]->getMessage());
1201 public function getTestReplaceDefaultGroup()
1205 'sequence' => new GroupSequence(array('Group 1', 'Group 2', 'Group 3', 'Entity')),
1206 'assertViolations' => array(
1207 'Violation in Group 2',
1211 'sequence' => array('Group 1', 'Group 2', 'Group 3', 'Entity'),
1212 'assertViolations' => array(
1213 'Violation in Group 2',
1217 'sequence' => new GroupSequence(array('Group 1', array('Group 2', 'Group 3'), 'Entity')),
1218 'assertViolations' => array(
1219 'Violation in Group 2',
1220 'Violation in Group 3',
1224 'sequence' => array('Group 1', array('Group 2', 'Group 3'), 'Entity'),
1225 'assertViolations' => array(
1226 'Violation in Group 2',
1227 'Violation in Group 3',