-
-
Notifications
You must be signed in to change notification settings - Fork 9.7k
[Validator] Add constraint on unique elements collection(Assert\Unique) #26555
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Validator\Constraints; | ||
|
||
use Symfony\Component\Validator\Constraint; | ||
|
||
/** | ||
* @Annotation | ||
* @Target({"PROPERTY", "METHOD", "ANNOTATION"}) | ||
* | ||
* @author Yevgeniy Zholkevskiy <zhenya.zholkevskiy@gmail.com> | ||
*/ | ||
class Unique extends Constraint | ||
{ | ||
const IS_NOT_UNIQUE = '7911c98d-b845-4da0-94b7-a8dac36bc55a'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
protected static $errorNames = array( | ||
self::IS_NOT_UNIQUE => 'IS_NOT_UNIQUE', | ||
); | ||
|
||
public $message = 'This collection should contain only unique elements'; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Validator\Constraints; | ||
|
||
use Symfony\Component\Validator\Constraint; | ||
use Symfony\Component\Validator\ConstraintValidator; | ||
use Symfony\Component\Validator\Exception\UnexpectedTypeException; | ||
|
||
/** | ||
* @author Yevgeniy Zholkevskiy <zhenya.zholkevskiy@gmail.com> | ||
*/ | ||
class UniqueValidator extends ConstraintValidator | ||
{ | ||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function validate($value, Constraint $constraint) | ||
{ | ||
if (!$constraint instanceof Unique) { | ||
throw new UnexpectedTypeException($constraint, Unique::class); | ||
} | ||
|
||
if (null === $value) { | ||
return; | ||
} | ||
|
||
if (!is_array($value) && !$value instanceof \IteratorAggregate) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've seen @ostrolucky 's #26555 (comment), but actually, we don't need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought so too until recently. Apparently this is considered too unsafe, see #24577 (comment) and #25506 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, IIUC that's an issue we already have with Was it already raised somewhere and do we need to add a deprecation to warn about this issue if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Dunno, what do you say @nicolas-grekas? I guess there should be an RFC to decide how to move forward in consistent way and let others to speak their mind about this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd keep it as it is. |
||
throw new UnexpectedTypeException($value, 'IteratorAggregate'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry but I think we should not throw here, to avoid issues like this one #26463 |
||
} | ||
|
||
$collectionElements = array(); | ||
foreach ($value as $element) { | ||
if (in_array($element, $collectionElements, true)) { | ||
$this->context->buildViolation($constraint->message) | ||
->setParameter('{{ value }}', $this->formatValue($value)) | ||
->setCode(Unique::IS_NOT_UNIQUE) | ||
->addViolation(); | ||
|
||
return; | ||
} | ||
$collectionElements[] = $element; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Validator\Tests\Constraints; | ||
|
||
use Symfony\Component\Validator\Constraints\Unique; | ||
use Symfony\Component\Validator\Constraints\UniqueValidator; | ||
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase; | ||
|
||
class UniqueValidatorTest extends ConstraintValidatorTestCase | ||
{ | ||
protected function createValidator() | ||
{ | ||
return new UniqueValidator(); | ||
} | ||
|
||
/** | ||
* @expectedException \Symfony\Component\Validator\Exception\UnexpectedTypeException | ||
*/ | ||
public function testExpectsUniqueConstraintCompatibleType() | ||
{ | ||
$this->validator->validate('', new Unique()); | ||
} | ||
|
||
/** | ||
* @dataProvider getValidValues | ||
*/ | ||
public function testValidValues($value) | ||
{ | ||
$this->validator->validate($value, new Unique()); | ||
|
||
$this->assertNoViolation(); | ||
} | ||
|
||
public function getValidValues() | ||
{ | ||
return array( | ||
yield 'null' => array(array(null)), | ||
yield 'empty array' => array(array()), | ||
yield 'single integer' => array(array(5)), | ||
yield 'single string' => array(array('a')), | ||
yield 'single object' => array(array(new \stdClass())), | ||
yield 'unique booleans' => array(array(true, false)), | ||
yield 'unique integers' => array(array(1, 2, 3, 4, 5, 6)), | ||
yield 'unique floats' => array(array(0.1, 0.2, 0.3)), | ||
yield 'unique strings' => array(array('a', 'b', 'c')), | ||
yield 'unique arrays' => array(array(array(1, 2), array(2, 4), array(4, 6))), | ||
yield 'unique objects' => array(array(new \stdClass(), new \stdClass())), | ||
); | ||
} | ||
|
||
/** | ||
* @dataProvider getInvalidValues | ||
*/ | ||
public function testInvalidValues($value) | ||
{ | ||
$constraint = new Unique(array( | ||
'message' => 'myMessage', | ||
)); | ||
$this->validator->validate($value, $constraint); | ||
|
||
$this->buildViolation('myMessage') | ||
->setParameter('{{ value }}', 'array') | ||
->setCode(Unique::IS_NOT_UNIQUE) | ||
->assertRaised(); | ||
} | ||
|
||
public function getInvalidValues() | ||
{ | ||
$object = new \stdClass(); | ||
|
||
return array( | ||
yield 'not unique booleans' => array(array(true, true)), | ||
yield 'not unique integers' => array(array(1, 2, 3, 3)), | ||
yield 'not unique floats' => array(array(0.1, 0.2, 0.1)), | ||
yield 'not unique string' => array(array('a', 'b', 'a')), | ||
yield 'not unique arrays' => array(array(array(1, 1), array(2, 3), array(1, 1))), | ||
yield 'not unique objects' => array(array($object, $object)), | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
UniqueCollection
?