-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Validator] Introduce BackedEnumValue
constraint
#54226
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?
[Validator] Introduce BackedEnumValue
constraint
#54226
Conversation
Enum
constraint
Did you read the discussion on #43047? |
Didn't see it before, my bad |
It's a dedicated constraint so why not but I understand if your point of view is to not accept it and adding the code below (as mentioned in the other discussion) if people want to accept an Enum value with public static function values(): array
{
return array_column(self::cases(), 'value');
} Because you are speaking about this topic, can you maybe give me feedback about this PR : symfony/symfony-docs#19590 (if it's added in the documentation, we will probably less try to find a way to achieve it) |
I didn't say that. It's just that there has been a PR on that topic a while ago, and if we revisit the topic, we should take that prior discussion into account. |
src/Symfony/Component/Validator/Tests/Constraints/EnumValidatorTest.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Tests/Constraints/EnumValidatorTest.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValueValidator.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValue.php
Outdated
Show resolved
Hide resolved
Enum
constraintBackedEnumValue
constraint
src/Symfony/Component/Validator/Constraints/BackedEnumValueValidator.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValueValidator.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValueValidator.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValueValidator.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValueValidator.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValue.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValue.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValue.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValueValidator.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValue.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValueValidator.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Tests/Constraints/BackedEnumValueValidatorTest.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValueValidator.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValue.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Validator/Constraints/BackedEnumValue.php
Outdated
Show resolved
Hide resolved
e48670d
to
edf0705
Compare
@fabpot @nicolas-grekas Can you tell me how to handle it properly? |
@AurelienPillevesse you should rebase your branch on top of the latest upstream 7.3 branch instead of merging. See https://symfony.com/doc/current/contributing/code/pull_requests.html#rebase-your-pull-request for the detailed explanation. |
d03a838
to
edf0705
Compare
edf0705
to
fb3fcd2
Compare
Thanks @stof |
parent::__construct([], $groups, $payload); | ||
|
||
if (!is_a($type, \BackedEnum::class, true)) { | ||
throw new ConstraintDefinitionException(\sprintf('The "type" must be a \BackedEnum, got "%s".', get_debug_type($type))); |
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.
throw new ConstraintDefinitionException(\sprintf('The "type" must be a \BackedEnum, got "%s".', get_debug_type($type))); | |
throw new ConstraintDefinitionException(\sprintf('The "type" must be a "\BackedEnum", got "%s".', get_debug_type($type))); |
) { | ||
parent::__construct([], $groups, $payload); | ||
|
||
if (!is_a($type, \BackedEnum::class, true)) { |
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.
if (!is_a($type, \BackedEnum::class, true)) { | |
if (!is_subclass_of($type, \BackedEnum::class, true)) { |
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.
is_a()
looks okay to me?
]; | ||
|
||
/** | ||
* @param class-string<\BackedEnum> $type the type of the enum |
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.
* @param class-string<\BackedEnum> $type the type of the enum | |
* @param class-string<\BackedEnum> $className the enum class name |
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.
first letter should be uppercase also
public string $type, | ||
public array $except = [], | ||
public string $message = 'The value you selected is not a valid choice.', | ||
public string $typeMessage = 'This value should be of type {{ type }}.', |
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.
public string $typeMessage = 'This value should be of type {{ type }}.', | |
public string $invalidValueTypeMessage = 'The value should be of type {{ type }}.', |
public function __construct( | ||
public string $type, | ||
public array $except = [], | ||
public string $message = 'The value you selected is not a valid choice.', |
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.
public string $message = 'The value you selected is not a valid choice.', | |
public string $invalidValueMessage = 'The value is not a backing value of {{ className }}.', |
->setParameter('{{ value }}', $this->formatValue($value)) | ||
->setParameter('{{ choices }}', $this->formatValidCases($constraint)) |
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.
@derrabus is right, you only need to define what is in the constraint message
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.
It's build in a similar way as Choice constraint.
Message :
public string $message = 'The value you selected is not a valid choice.'; |
Validator :
symfony/src/Symfony/Component/Validator/Constraints/ChoiceValidator.php
Lines 101 to 105 in 78f4d9a
$this->context->buildViolation($constraint->message) | |
->setParameter('{{ value }}', $this->formatValue($value)) | |
->setParameter('{{ choices }}', $this->formatValues($choices)) | |
->setCode(Choice::NO_SUCH_CHOICE_ERROR) | |
->addViolation(); |
The value
or choice
values are not used by default. If you override the default message and use {{ value }}
or {{ choice }}
on it, it will be replaced in your new message.
} | ||
|
||
try { | ||
$enumTypeValue = $constraint->type::tryFrom($value); |
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.
$enumTypeValue = $constraint->type::tryFrom($value); | |
$enumValue = $constraint->type::tryFrom($value); |
return $this->formatValues(array_map( | ||
static fn (\BackedEnum $case) => $case->value, | ||
array_filter( | ||
$constraint->type::cases(), | ||
static fn (\BackedEnum $currentValue) => !\in_array($currentValue, $constraint->except, true), | ||
) | ||
)); |
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.
return $this->formatValues(array_map( | |
static fn (\BackedEnum $case) => $case->value, | |
array_filter( | |
$constraint->type::cases(), | |
static fn (\BackedEnum $currentValue) => !\in_array($currentValue, $constraint->except, true), | |
) | |
)); | |
return $this->formatValues(array_column( | |
array_filter( | |
$constraint->type::cases(), | |
static fn (\BackedEnum $currentValue) => !\in_array($currentValue, $constraint->except, true), | |
), | |
'value', | |
)); |
I think this change requires setting
NotNormalizableValueException unless ALLOW_INVALID_VALUES is explicitly set to true in the context, which does not make sense IMO.
|
@sfmok this PR is totally unrelated to that normalizer. The constraint added here is meant to be used in case your DTO expects a scalar value that must match the value associated with a case of the backed enum. The |
My use case: I receive data in my DTOs that we only want if this value exists in an Enum and reject the whole thing at validation otherwise.