-
-
Notifications
You must be signed in to change notification settings - Fork 504
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
add BackedEnum support #2412
add BackedEnum support #2412
Conversation
598dd69
to
76d0553
Compare
This is now ready for review. Please note that the "required" Coding Standards run will not be executed because I changed the PHP version there so the required name no longer match repository settings. Also, it will produce conflicts with #2411 - depending on which PR is merged first, the other one must be updated to bring typed property guessing for enums. |
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.
Great you took care of porting adding the feature! Beside my remarks above please also add a relevant documentation.
Food for thought: in the past it was requested that we pass $mapping
to types. While I'm still reluctant to do so it'd make the implementation easier as we'd just add new enum
type that would read target enum from the mapping. Thoughts on this?
*/ | ||
public function __construct( | ||
?string $name = null, | ||
?string $type = 'string', |
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 need to remember to change the default value here as #2411 did
@@ -275,6 +275,10 @@ public function loadMetadataForClass($className, \Doctrine\Persistence\Mapping\C | |||
$mapping['notSaved'] = ((string) $attributes['not-saved'] === 'true'); | |||
} | |||
|
|||
if (isset($mapping['enum-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.
This looks fishy given the check above and below are checking (and assigning from) the $attributes
array.
public static function nonEnumTypeMapped(string $className, string $fieldName, string $enumType): self | ||
{ | ||
return new self(sprintf( | ||
'Attempting to map non-enum type %s as enum in document class %s::$%s', |
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.
'Attempting to map non-enum type %s as enum in document class %s::$%s', | |
'Attempting to map a non-enum type %s as an enum: %s::%s', |
This should be the most consistent with other exceptions we have
|
||
public static function enumsRequirePhp81(string $className, string $fieldName): self | ||
{ | ||
return new self(sprintf('Enum types require PHP 8.1 in %s::$%s', $className, $fieldName)); |
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 new self(sprintf('Enum types require PHP 8.1 in %s::$%s', $className, $fieldName)); | |
return new self(sprintf('Enum types require PHP 8.1 in %s::%s', $className, $fieldName)); |
We don't use $
in other exceptions
ValueError $previous | ||
): self { | ||
return new self(sprintf( | ||
'Trying to hydrate enum property "%s::$%s". Problem: Case "%s" is not listed in enum "%s"', |
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.
'Trying to hydrate enum property "%s::$%s". Problem: Case "%s" is not listed in enum "%s"', | |
'Trying to hydrate an enum property %s::%s, but "%s" is not listed as one of "%s" cases', |
* @param class-string $className | ||
* @param class-string<BackedEnum> $enumType | ||
*/ | ||
public static function invalidEnumValue( |
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 one is definitely not a mapping exception but rather a https://github.com/doctrine/mongodb-odm/blob/2.3.x/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorException.php
use function is_int; | ||
use function is_string; | ||
|
||
class ReflectionEnumProperty extends ReflectionProperty |
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 a pity this class did not went into a common repository. If you're not willing to pursue this then it should be final and marked as @internal
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.
@beberlei do you have any insights why ReflectionEnumProperty isn't a part of https://github.com/doctrine/persistence/tree/2.3.x/lib/Doctrine/Persistence/Reflection?
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.
While waiting for doctrine/common merge and release we could add this class (final and internal). Once doctrine/common is there we'll change implementation :)
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.
@malarzm What about the exception thrown? The doctrine/persistence
one throws native ValueError
, while here we are converting it to a HydratorException
which is not available in doctrine/persistence
. I suggest simplifying this implementation to throw native error instead too for easier migration.
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.
The HydratorInterface doesn't make any promises on what exceptions can be thrown, but generally it throws HydratorException when something is off with data (https://github.com/doctrine/mongodb-odm/blob/2.3.x/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php#L333 and more, look for throw
). If possible and relatively simple I'd wrap native ValueError with our HydratorException. If it's not simple then let's the native error be thrown, we can't save users from everything :)
<document name="Documents81\Card"> | ||
<id /> | ||
<field name="suit" type="string" enum-type="Documents81\Suit" /> | ||
<field name="nullableSuit" type="string" enum-type="Documents81\Suit" nullable="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.
When we go down type-inferring rule we'll need to think how to correctly choose string vs int for the type conversion.
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 can check if the enum class implements StringBackedEnum
or IntBackedEnum
to detect the underlying type.
cf4e5b2
to
0184214
Compare
#2411 was merged, this needs to be rebased (it's conflicting anyway) and adapted :) |
8fb701f
to
ac9913e
Compare
@malarzm rebased and adjusted accordingly. I will keep it as separate commits for cleaner diffs but I'll squash them once approved. |
Since |
0d5ce15
to
44dd61d
Compare
Co-authored-by: Fran Moreno <franmomu@gmail.com>
44dd61d
to
0c6d320
Compare
@@ -2556,6 +2575,15 @@ private function validateAndCompleteTypedFieldMapping(array $mapping): array | |||
return $mapping; | |||
} | |||
|
|||
if (PHP_VERSION_ID >= 80100 && ! $type->isBuiltin() && enum_exists($type->getName(), 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.
Please add this feature to documentation https://github.com/doctrine/mongodb-odm/pull/2411/files#diff-239d9a128ce24a76e204d8b9821ce009ef5c7b16298d549165da9bc05d5d8f97 created
$reflection = new ReflectionEnum($type->getName()); | ||
$type = $reflection->getBackingType(); | ||
|
||
assert($type instanceof ReflectionNamedType); |
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.
null
case is not handled (assert is just for PHPStan). Using a non-backed enum should warrant an exception
What's need to finish this PR ? Do you need help @IonBazan ? |
FWIW I'll be wrapping this up over the weekend. |
Thanks @malarzm for taking over. Sorry, I didn't have enough time to make it prod-ready. PS. Let me know if you want me to squash anything before you add your changes. |
Merged in #2440 |
Summary
Provides native support forBackedEnum
s. This could be later integrated with #2411 to allow even better DX.Already integrated in this PR 🚀
Additional changes:
case
indentation due to PHP 8.1enum
not recognized squizlabs/PHP_CodeSniffer#3474