Skip to content

Commit

Permalink
bug #54137 [Validator] UniqueValidator - normalize before reducing ke…
Browse files Browse the repository at this point in the history
…ys (Brajk19)

This PR was squashed before being merged into the 6.4 branch.

Discussion
----------

[Validator] UniqueValidator - normalize before reducing keys

| Q             | A
| ------------- | ---
| Branch?       | 6.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Issues        |
| License       | MIT

In #42403 checking for uniqueness of certain collection keys was enabled. Method `UniqueValidator::reduceElementKeys` removes all keys which are not specified.
Problem is that this happens before normalization, which in my opinion is not great because that method accepts array argument and if i have some object (DTO), TypeError will be thrown.

Example:

```php
class ParentDTO
{
    /**
     * `@var` ChildDTO[]
     */
    #[Assert\Unique(
        normalizer: [ChildDTO::class, 'normalize']
        fields: 'id'
    )]
    public array $children;
}
```

```php
class ChildDTO
{
    public string $id;
    public string $name;

    public static function normalize(self $obj): array
    {
        return [
            'id' => $obj->id,
            'name' => $obj->name
        ];
    }
}
```

Because normalization will happen after `reduceElementKeys` this will be thrown:
`TypeError: Symfony\Component\Validator\Constraints\UniqueValidator::reduceElementKeys(): Argument #2 ($element) must be of type array, ...\ChildDTO given, called in .../UniqueValidator.php on line 48`

If `$element = $normalizer($element);` is executed before `reduceElementKeys` it would enable using Assert\Unique with array of objects when correctly normalized

Commits
-------

77df90b [Validator] UniqueValidator - normalize before reducing keys
  • Loading branch information
fabpot committed Mar 9, 2024
2 parents 5dbc82d + 77df90b commit 04652b7
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,12 @@ public function validate(mixed $value, Constraint $constraint)
$collectionElements = [];
$normalizer = $this->getNormalizer($constraint);
foreach ($value as $element) {
$element = $normalizer($element);

if ($fields && !$element = $this->reduceElementKeys($fields, $element)) {
continue;
}

$element = $normalizer($element);

if (\in_array($element, $collectionElements, true)) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;
use Symfony\Component\Validator\Tests\Dummy\DummyClassOne;

class UniqueValidatorTest extends ConstraintValidatorTestCase
{
Expand Down Expand Up @@ -283,6 +284,36 @@ public static function getInvalidCollectionValues(): array
],
];
}

public function testArrayOfObjectsUnique()
{
$array = [
new DummyClassOne(),
new DummyClassOne(),
new DummyClassOne(),
];

$array[0]->code = '1';
$array[1]->code = '2';
$array[2]->code = '3';

$this->validator->validate(
$array,
new Unique(
normalizer: [self::class, 'normalizeDummyClassOne'],
fields: 'code'
)
);

$this->assertNoViolation();
}

public static function normalizeDummyClassOne(DummyClassOne $obj): array
{
return [
'code' => $obj->code,
];
}
}

class CallableClass
Expand Down

0 comments on commit 04652b7

Please sign in to comment.