[Validator] Add constraint on unique elements collection(Assert\Unique)#26555
[Validator] Add constraint on unique elements collection(Assert\Unique)#26555fabpot merged 2 commits intosymfony:masterfrom
Conversation
zzenmate
commented
Mar 15, 2018
| Q | A |
|---|---|
| Branch? | master |
| Bug fix? | no |
| New feature? | yes |
| BC breaks? | no |
| Deprecations? | no |
| Tests pass? | no |
| Fixed tickets | #26535 |
| License | MIT |
| Doc PR | symfony/symfony-docs#... |
|
Please, review feature. |
| return; | ||
| } | ||
|
|
||
| if (!is_array($value) && !($value instanceof \Traversable && $value instanceof \ArrayAccess)) { |
There was a problem hiding this comment.
Scrap ($value instanceof \Traversable && $value instanceof \ArrayAccess) and replace with !$value instanceof \IteratorAggregate. It's apparently interface of Traversable highest up in a chain which guarantees iterator is rewindable
|
|
||
| $collectionElements = []; | ||
| foreach ($value as $element) { | ||
| if (in_array($element, $collectionElements)) { |
There was a problem hiding this comment.
third argument to true is important here to avoid obscure bugs https://3v4l.org/9QGNo
| public function validate($value, Constraint $constraint) | ||
| { | ||
| if (!$constraint instanceof UniqueCollection) { | ||
| throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\UniqueCollection'); |
| * | ||
| * @author Yevgeniy Zholkevskiy <[email protected]> | ||
| */ | ||
| class UniqueCollection extends Constraint |
There was a problem hiding this comment.
Symfony constraints don't tend to add such suffix. See e.g. constraint "All". Should be just Unique. Would be in line with #9888 as well.
|
Would wait for a symfony team member to confirm, but I think this is pretty straight forward and feature should be accepted. status: needs review |
| return; | ||
| } | ||
|
|
||
| if (!$value instanceof \IteratorAggregate) { |
There was a problem hiding this comment.
is_array part must stay, I wanted to change second part of condition only. This won't work with arrays now.
|
@zzenmate please check fabbot's failure |
ogizanagi
left a comment
There was a problem hiding this comment.
Thanks for your contribution!
| foreach ($value as $element) { | ||
| if (in_array($element, $collectionElements, true)) { | ||
| $this->context->buildViolation($constraint->message) | ||
| ->setParameter('{{ value }}', $this->formatValue($value)) |
There was a problem hiding this comment.
Additionnally to fabbot reported issues, there is too much indentation here:
$this->context->buildViolation($constraint->message)
- ->setParameter('{{ value }}', $this->formatValue($value))
- ->setCode(Unique::IS_NOT_UNIQUE)
- ->addViolation();
+ ->setParameter('{{ value }}', $this->formatValue($value))
+ ->setCode(Unique::IS_NOT_UNIQUE)
+ ->addViolation();| return; | ||
| } | ||
|
|
||
| if (!is_array($value) && !$value instanceof \IteratorAggregate) { |
There was a problem hiding this comment.
I've seen @ostrolucky 's #26555 (comment), but actually, we don't need ArrayAccess here, right?
So !is_array($value) && !$value instanceof \Traversable would be enough.
There was a problem hiding this comment.
I thought so too until recently. Apparently this is considered too unsafe, see #24577 (comment) and #25506
There was a problem hiding this comment.
So, IIUC that's an issue we already have with AllValidator then?
Was it already raised somewhere and do we need to add a deprecation to warn about this issue if $value isn't implementing IteratorAggregate?
There was a problem hiding this comment.
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.
| $collectionElements[] = $element; | ||
| } | ||
| } | ||
| } No newline at end of file |
|
status: needs review |
ostrolucky
left a comment
There was a problem hiding this comment.
This test suite is IMO too noisy. Check e.g. RegexValidatorTest to learn how to utilize dataProviders to shrink this.
|
@ostrolucky Thank you for your advice! I have done it. |
| } | ||
|
|
||
| if (!is_array($value) && !$value instanceof \IteratorAggregate) { | ||
| throw new UnexpectedTypeException($value, 'IteratorAggregate'); |
There was a problem hiding this comment.
Sorry but I think we should not throw here, to avoid issues like this one #26463
| */ | ||
| class Unique extends Constraint | ||
| { | ||
| const IS_NOT_UNIQUE = '7911c98d-b845-4da0-94b7-a8dac36bc55a'; |
| return; | ||
| } | ||
|
|
||
| if (!is_array($value) && !$value instanceof \IteratorAggregate) { |
|
oh, please also mention the new constraint in the changelog of the component |
|
Friendly ping @zzenmate, could you please add an entry in the changelog file of the component? |
|
And create a doc issue/PR also please? |
ad8e7d4 to
bfa3278
Compare
bfa3278 to
d0eb13e
Compare
|
Rebased, ready. |
|
Thank you @zzenmate. |
…on(Assert\Unique) (zenmate, nicolas-grekas) This PR was merged into the 4.3-dev branch. Discussion ---------- [Validator] Add constraint on unique elements collection(Assert\Unique) | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | no <!-- please add some, will be required by reviewers --> | Fixed tickets | #26535 | License | MIT | Doc PR | symfony/symfony-docs#... <!-- required for new features --> <!-- Write a short README entry for your feature/bugfix here (replace this comment block.) This will help people understand your PR and can be used as a start of the Doc PR. Additionally: - Bug fixes must be submitted against the lowest branch where they apply (lowest branches are regularly merged to upper ones so they get the fixes too). - Features and deprecations must be submitted against the master branch. --> Commits ------- d0eb13e Rebase and update to latest CS fc66683 Add UniqueCollection constraint and validator
| } | ||
|
|
||
| if (!\is_array($value) && !$value instanceof \IteratorAggregate) { | ||
| throw new UnexpectedValueException($value, 'array|IteratorAggregate'); |
There was a problem hiding this comment.
I thought so too until recently. Apparently this is considered too unsafe, see #24577 (comment) and #25506
?
was it a blocker for this PR?
There was a problem hiding this comment.
i can pass a broken/unrewindbable iterator as aggregate just as easy. But i cant pass a working iterator/iterable directly. That's poor DX IMHO.
There was a problem hiding this comment.
Consequencies of this validator being applied on non-rewindable iterators are much bigger than in #25506 so yes (generally, after validator validates the data, data is processed)
| * | ||
| * @author Yevgeniy Zholkevskiy <[email protected]> | ||
| */ | ||
| class Unique extends Constraint |