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
[Validator] Introduce BackedEnumValue
constraint
#54226
base: 7.1
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/BackedEnumValueValidator.php
Outdated
Show resolved
Hide resolved
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 like what we've done here. It's a simple self-contained constraint that should settle the use-cases people described in #43047.
use Symfony\Component\Validator\Exception\ConstraintDefinitionException; | ||
|
||
/** | ||
* Validates that a value is one of an 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.
* Validates that a value is one of an enum. | |
* Validates that a backed enum can be hydrated from a value. |
We should add a piece of documentation here, that directs people to Type
and Choice
constraints, if they want to validate actual enums instead the backing values. The whole discussion in #43047 shows that people mix this up regularly.
use Symfony\Component\Validator\Exception\UnexpectedTypeException; | ||
|
||
/** | ||
* BackedEnumValueValidator validates that the value is one of the expected values. |
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.
* BackedEnumValueValidator validates that the value is one of the expected values. | |
* BackedEnumValueValidator validates that a backed enum case can be hydrated from a value. |
b57181b
to
6635e73
Compare
Thanks for your feedbacks during this PR @derrabus |
$this->validator->validate( | ||
'yes', | ||
new BackedEnumValue( | ||
type: MyStringEnum::class |
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.
This should not reference non-autoloadable enums defined in a different test file as this would break if the other test file has not been loaded
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.
Fixed
6635e73
to
a6ae67b
Compare
src/Symfony/Component/Validator/Constraints/BackedEnumValue.php
Outdated
Show resolved
Hide resolved
This looks good, hopefully this gets through this time! |
a6ae67b
to
b0a1c63
Compare
If you want me to do any other change here, please tell me. Otherwise, please review and approve this PR so maintainers can see it as ready. Thanks! |
I looked at it and it looks good, just one minor thing about the location of the fixture enums in the tests. Not going to reject it based on that, nor approve it. Don't know what is recommended right now. Once that is cleared up I'll approve it if that helps 👍🏻 Great work! |
return; | ||
} | ||
|
||
try { |
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.
try { | |
if ($value instanceof \Stringable) { | |
$value = (string) $value; | |
} | |
if (!\is_string($value)) { | |
throw new UnexpectedValueException($value, 'string'); | |
} | |
try { |
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.
backed enums can be ints, im not sure it's covered in tests
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.
int
enum case is covered in tests :
symfony/src/Symfony/Component/Validator/Tests/Constraints/BackedEnumValueValidatorTest.php
Line 141 in b0a1c63
enum MyIntBackedEnum: int |
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.
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 checking the type would free us from catching the TypeError
, I'd say go for it. But given that we still need to catch it, I would not go with @xabbuh's proposal.
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 depends if we want to expose other types than string|int as a user-error
unexpected types should throw UnexpectedValueException, following the component's design
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.
So we catch the TypeError
and upcast it?
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.
yes, we could differentiate between TypeError
and ValueError
, respectively translating to UnexpectedValueException
and a user constraint error in symfony
but then we should use BackedEnum::from()
AFAIK, because tryFrom
seems really quirky: https://3v4l.org/54qFs
edit: oh tryFrom/from
is prone to strict_types=1
*/ | ||
class BackedEnumValueTest extends TestCase | ||
{ | ||
public function testAttributes() |
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.
we should also add tests covering the cases that throw exceptions
throw new UnexpectedTypeException($constraint, BackedEnumValue::class); | ||
} | ||
|
||
if (null === $value || '' === $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.
i think the empty string case should pass through https://3v4l.org/lu4o4
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.
Empty values are always valid and that's what this check does. This is fine.
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 think generally so, it's an edge case
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.
No, your "edge case" simply does not make a difference.
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.
allowing empty string, but only when it's a valid enum case, is not possible currently
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.
see also Choice constraint
if (null === $value) { | |
return; | |
} |
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.