Skip to content

Commit fc66683

Browse files
zenmatenicolas-grekas
zenmate
authored andcommitted
Add UniqueCollection constraint and validator
1 parent 29f81b0 commit fc66683

File tree

3 files changed

+173
-0
lines changed

3 files changed

+173
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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+
16+
/**
17+
* @Annotation
18+
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
19+
*
20+
* @author Yevgeniy Zholkevskiy <[email protected]>
21+
*/
22+
class Unique extends Constraint
23+
{
24+
const IS_NOT_UNIQUE = '7911c98d-b845-4da0-94b7-a8dac36bc55a';
25+
26+
protected static $errorNames = array(
27+
self::IS_NOT_UNIQUE => 'IS_NOT_UNIQUE',
28+
);
29+
30+
public $message = 'This collection should contain only unique elements';
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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 Yevgeniy Zholkevskiy <[email protected]>
20+
*/
21+
class UniqueValidator extends ConstraintValidator
22+
{
23+
/**
24+
* {@inheritdoc}
25+
*/
26+
public function validate($value, Constraint $constraint)
27+
{
28+
if (!$constraint instanceof Unique) {
29+
throw new UnexpectedTypeException($constraint, Unique::class);
30+
}
31+
32+
if (null === $value) {
33+
return;
34+
}
35+
36+
if (!is_array($value) && !$value instanceof \IteratorAggregate) {
37+
throw new UnexpectedTypeException($value, 'IteratorAggregate');
38+
}
39+
40+
$collectionElements = array();
41+
foreach ($value as $element) {
42+
if (in_array($element, $collectionElements, true)) {
43+
$this->context->buildViolation($constraint->message)
44+
->setParameter('{{ value }}', $this->formatValue($value))
45+
->setCode(Unique::IS_NOT_UNIQUE)
46+
->addViolation();
47+
48+
return;
49+
}
50+
$collectionElements[] = $element;
51+
}
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Tests\Constraints;
13+
14+
use Symfony\Component\Validator\Constraints\Unique;
15+
use Symfony\Component\Validator\Constraints\UniqueValidator;
16+
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
17+
18+
class UniqueValidatorTest extends ConstraintValidatorTestCase
19+
{
20+
protected function createValidator()
21+
{
22+
return new UniqueValidator();
23+
}
24+
25+
/**
26+
* @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException
27+
*/
28+
public function testExpectsUniqueConstraintCompatibleType()
29+
{
30+
$this->validator->validate('', new Unique());
31+
}
32+
33+
/**
34+
* @dataProvider getValidValues
35+
*/
36+
public function testValidValues($value)
37+
{
38+
$this->validator->validate($value, new Unique());
39+
40+
$this->assertNoViolation();
41+
}
42+
43+
public function getValidValues()
44+
{
45+
return array(
46+
yield 'null' => array(array(null)),
47+
yield 'empty array' => array(array()),
48+
yield 'single integer' => array(array(5)),
49+
yield 'single string' => array(array('a')),
50+
yield 'single object' => array(array(new \stdClass())),
51+
yield 'unique booleans' => array(array(true, false)),
52+
yield 'unique integers' => array(array(1, 2, 3, 4, 5, 6)),
53+
yield 'unique floats' => array(array(0.1, 0.2, 0.3)),
54+
yield 'unique strings' => array(array('a', 'b', 'c')),
55+
yield 'unique arrays' => array(array(array(1, 2), array(2, 4), array(4, 6))),
56+
yield 'unique objects' => array(array(new \stdClass(), new \stdClass())),
57+
);
58+
}
59+
60+
/**
61+
* @dataProvider getInvalidValues
62+
*/
63+
public function testInvalidValues($value)
64+
{
65+
$constraint = new Unique(array(
66+
'message' => 'myMessage',
67+
));
68+
$this->validator->validate($value, $constraint);
69+
70+
$this->buildViolation('myMessage')
71+
->setParameter('{{ value }}', 'array')
72+
->setCode(Unique::IS_NOT_UNIQUE)
73+
->assertRaised();
74+
}
75+
76+
public function getInvalidValues()
77+
{
78+
$object = new \stdClass();
79+
80+
return array(
81+
yield 'not unique booleans' => array(array(true, true)),
82+
yield 'not unique integers' => array(array(1, 2, 3, 3)),
83+
yield 'not unique floats' => array(array(0.1, 0.2, 0.1)),
84+
yield 'not unique string' => array(array('a', 'b', 'a')),
85+
yield 'not unique arrays' => array(array(array(1, 1), array(2, 3), array(1, 1))),
86+
yield 'not unique objects' => array(array($object, $object)),
87+
);
88+
}
89+
}

0 commit comments

Comments
 (0)