-
-
Notifications
You must be signed in to change notification settings - Fork 505
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
Allow using enums directly in queries #2447
Conversation
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 do note that if enumType is not set, the value is passed directly to the Type without any changes
Thanks to this I'm inclined to release this PR as a bugfix :instead of hacing people wait until next minor 🎉 If anybody asks let's say we thought it'll work out of the box 🙈
@@ -1144,6 +1149,10 @@ private function convertToDatabaseValue(string $fieldName, $value) | |||
); | |||
} | |||
|
|||
if ($value instanceof BackedEnum && $mapping['enumType']) { |
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.
Given $mapping['enumType']
can be either null
or a backed enum's FQCN (guaranteed by metadata validation) we should be OK to do following check (maybe backed by an \assert
call to satisfy SA) which should be cheaper
if ($value instanceof BackedEnum && $mapping['enumType']) { | |
if (isset($mapping['enumType'])) { |
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.
Also I'm not sure about the call being here instead of after next if
statement. Actually I started wondering if we shouldn't ensure that enumType
can be combined with type=int|string
(or not set which allows autoconfiguration to kick in). Right now
/** @ODM\Field(type="collection", enumType=Suit::class) */
private array $enums = [];
looks OK from user's perspective but I believe it'll blow up?
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 $value instanceof BackedEnum
is necessary because the value might be the enum's backed value too:
$this->dm->createQueryBuilder(Card::class)
->field('suit')->equals(Suit::Spades->value)
->field('nullableSuit')->equals('C');
I'll add a test for that.
Regarding mixing enumType
with arrays, it will probably blow up if you map it this way. Not sure how could we solve it here though.
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.
Not sure how could we solve it here though.
Not necessarily here, I'll hack a PR later today to throw a mapping exception if enumType
is combined with something else than int
or string
types. Then we'll have a clear "it's not working message" for userland instead of weird blow ups under the hood.
Is this PR in any way related to the driver? We recently had a bug report come in about using enums in conjunction with the driver's I'm tracking this enum/Persistable issue in PHPC-2083 and have started working on a PR. I haven't actually tested standard BSON serialization for enums, but based on this issue it seems like they get encoded as BSON documents with Would it be preferable for the driver to raise an exception when the user attempts to serialize a pure enum (without Persistable) and encode a BackedEnum as its value (i.e. BSON integer or string)? |
No, we ended with our own (ok ok, ORM's 🙄) way of handling enums (#2412): user must either specify an
With ODM we've decided to deal only with
Encoding any backed enum as its backing value would save us some headaches that we are yet to discover, maybe they'd render this PR not needed as well. But is the behaviour safe from driver's perspective? I mean we're cool saving just the value because we have a mapping to know what enum instatiate later. I guess same could be done with an explicit typeMap |
21f0c9e
to
9c9f90f
Compare
@IonBazan please squash commits ;) |
9c9f90f
to
237766a
Compare
Thanks @IonBazan! |
@IonBazan: I haven't worked enough with enums to say with any certainty, but the documentation does say that backed enums exist to allow for database round-tripping. I assume that is alluding to ORM logic that would re-construct an enum from mapping information given a value (using the IMO, allowing a pure or backed enum to be serialized to BSON is preferable from a driver perspective (assuming users opt in via the Persistable interface), since it's less for the application or an ORM to worry about; however, that does invite possible issues converting BSON to PHP if the document data references a class that no longer exists in the application. Enums just introduce some additional ways that code changes could break if a name or value is no longer defined. |
Summary
This change allows using
BackedEnum
s directly in the query builder instead of using->value
:This supports arrays too. Please do note that if
enumType
is not set, the value is passed directly to theType
without any changes to allow developers to customize the behaviour in their custom types if needed (seetestQueryWithMappedNonEnumFieldIsPassedToTypeDirectly()
where value is passed directly toStringType
and producing an error).I've added the tests in
EnumTest
instead ofDocumentPersisterTest
to avoid conditional mapping loading there (this feature requires PHP 8.1+).Not sure if this should be treated as bugfix or improvement so feel free to assign to correct milestone.