-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Validator] Unique should support objects fields #47714
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
base: 7.3
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,8 +11,12 @@ | |
|
||
namespace Symfony\Component\Validator\Constraints; | ||
|
||
use Symfony\Component\PropertyAccess\PropertyAccess; | ||
use Symfony\Component\PropertyAccess\PropertyAccessor; | ||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface; | ||
use Symfony\Component\Validator\Constraint; | ||
use Symfony\Component\Validator\ConstraintValidator; | ||
use Symfony\Component\Validator\Exception\LogicException; | ||
use Symfony\Component\Validator\Exception\UnexpectedTypeException; | ||
use Symfony\Component\Validator\Exception\UnexpectedValueException; | ||
|
||
|
@@ -21,6 +25,13 @@ | |
*/ | ||
class UniqueValidator extends ConstraintValidator | ||
{ | ||
private ?PropertyAccessorInterface $propertyAccessor; | ||
|
||
public function __construct(PropertyAccessorInterface $propertyAccessor = null) | ||
{ | ||
$this->propertyAccessor = $propertyAccessor; | ||
} | ||
|
||
public function validate(mixed $value, Constraint $constraint) | ||
{ | ||
if (!$constraint instanceof Unique) { | ||
|
@@ -69,18 +80,37 @@ private function getNormalizer(Unique $unique): callable | |
return $unique->normalizer; | ||
} | ||
|
||
private function reduceElementKeys(array $fields, array $element): array | ||
private function reduceElementKeys(array $fields, array|object $element): array | ||
{ | ||
$output = []; | ||
foreach ($fields as $field) { | ||
if (!\is_string($field)) { | ||
throw new UnexpectedTypeException($field, 'string'); | ||
} | ||
if (isset($element[$field])) { | ||
$output[$field] = $element[$field]; | ||
|
||
// For no BC, because PropertyAccessor require brackets for array keys | ||
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. For no BC, that simple case should be handled without requiring PropertyAccess 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. In a first time this is what I've done: check if element is an array, then that the key do not contains any "[]." (PropertyAccessor syntax) and the key exists in the element. In that case directly access to the array value via the key. And add an Else statement (not array and/or PropertyPath syntax, that contains the if statement (because the actual implementation accept that the key can be missing). At the end, find simpler to just transform the key in PrpertyPath compliant syntax. |
||
// Previous implementation, only check in array | ||
if (\is_array($element) && !str_contains($field, '[')) { | ||
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 do this: if (is_object($element)) {
if (!class_exists(PropertyAccess::class)) {
throw new LogicException('Using UniqueValidator on object requires "PropertyAccess" component. Try running "composer require symfony/property-access".');
}
$this->accessor ??= PropertyAccess::createPropertyAccessor();
if ($accessor->isReadable($element, $field)) {
$output[$field] = $accessor->getValue($element, $field);
}
} 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 don't think validating deeper than first level should be allowed. |
||
$field = "[{$field}]"; | ||
} | ||
|
||
if (null !== $value = $this->getPropertyAccessor()->getValue($element, $field)) { | ||
$output[$field] = $value; | ||
} | ||
} | ||
|
||
return $output; | ||
} | ||
|
||
private function getPropertyAccessor(): PropertyAccessor | ||
{ | ||
if (null === $this->propertyAccessor) { | ||
if (!class_exists(PropertyAccess::class)) { | ||
throw new LogicException('Unable to use property path as the Symfony PropertyAccess component is not installed.'); | ||
} | ||
$this->propertyAccessor = PropertyAccess::createPropertyAccessor(); | ||
} | ||
|
||
return $this->propertyAccessor; | ||
} | ||
} |
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.
I'd configure
factor
for this method and onInvalid set null, otherwise it does not make any sense because PropertyAccess does not public constructor and only way to create Accessor is to call static methodcreatePropertyAccessor