-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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 support for PHP 8.1 enums. #9304
Conversation
This looks like the most promising solution I've seen so far, thanks! |
For phpcs.xml.dist: <rule ref="Generic.WhiteSpace.ScopeIndent.Incorrect">
<!-- see https://github.com/squizlabs/PHP_CodeSniffer/issues/3474 -->
<exclude-pattern>your/path/here</exclude-pattern>
</rule>
<rule ref="Generic.WhiteSpace.ScopeIndent.IncorrectExact">
<!-- see https://github.com/squizlabs/PHP_CodeSniffer/issues/3474 -->
<exclude-pattern>your/path/here</exclude-pattern>
</rule> If you add the paths to your enum files here, PHPCS should allow you to fix the indentation manually. |
I think the following error scenarios should be covered by tests:
Furthermore, I think we should be able to detect |
The |
<UndefinedDocblockClass>
<errorLevel type="suppress">
<referencedClass name="BackedEnum"/>
</errorLevel>
</UndefinedDocblockClass> |
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.
Nice! This does look more straightforward than other approaches.
3 things:
- should there be checks regarding the type? What should happen if an enum backed by an int is used within a mapping that uses a stringy type? And vice versa?
- should we stop adding features to the YAML and Annotation drivers? They are deprecated.
- there should be docs
One more reason why it makes sense to implement this at the ORM level: what we obtain in our mapped property is a user-defined type (the enum), just like embedded classes, and those are not implemented in the DBAL.
@greg0ire about your bullets:
|
|
@filiplikavcan the tests usually go for the full information, you can just declare it as the type itself and Doctrine's automatic type completion will do that: #[Column]
public Suit $suit; |
I have everything working now exception Psalm not detecting my ignoring of |
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 just created #9310 to make the baseline changes unnecessary (will require a merge up from 2.10 to 2.11)
I've modified |
[$metadata->fieldMappings['id']['columnName'] => $card->id] | ||
); | ||
|
||
$this->expectException(ValueError::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.
I think we should catch and upcast this error. There is something wrong with the user's database and the user should be made aware of that. DBAL has a ConversionException
for errors like this one. Either we reuse that or we declare our own exception for that.
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 we should have our own one, it would be less confusing. The message could look like this:
Context: trying to hydrate Property "MyClass::$foo"
Problem: That property is of enum type "My\Enum", which does not accept value "value"
Solution: migrate the database value to a supported case or add a new case to the enum
It does make Psalm green, but now Phpstan is red… for reasons that have to do with the last release of the DBAL (2.10.x is also broken) |
Thanks for this great feature! |
A much awaited feature! Well done everyone! |
Thank you a lot for this feature, I have already implemented it and works without a problem. Wish list: class BiomarkerRange
{
public function __construct(
private AgeCategoryEnum $ageCategory,
) {} If defined like this: <field name="ageCategory" enum-type="AgeCategoryEnum"/> everything works perfectly. But when I drop
It is not a big deal if not possible, I am still very happy even if I have to put it. But it would make it more consistent with other guessers. |
Type guessing is currently only implemented for attributes. Please open an issue if you want to extend the feature to other mapping types, but my gut feeling is that this is a bad idea. |
@beberlei You didn't write the docs yet, did you? If you want, I could start a PR, but I'd need help from you, since I don't know all the details. |
Just to be clear for this PR, are non-backed enums supported? Will the name value be stored as a string value? |
Non backed enums are not supported, there is no value to store them with. |
The 'value' is the name of the case, surely? |
I would have to side with @sxsws on this. To quote PHP manual:
This would suggest to me that it's a valid, storable, value. The caveat is that the value matches the This is different from the <?php
enum Suit: string
{
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}
?>
// other documentation
<?php
print Suit::Clubs->value;
// Prints "C"
?> Another difference is, the value of a Differentiating between the can be done by using new This page also shows the existance of static method <?php
Suit::cases();
// Produces: [Suit::Hearts, Suit::Diamonds, Suit::Clubs, Suit:Spades]
?> For mapping to a The above scalar mapping for Having said all of the above, I'm excited already with just BackedEnums :) |
It clearly says "useful for debugging purposes", not "a fallback if there is not backed value".
Probably, but it's not meant for storing. I've heard alternative proposals that use the fully qualified name of the case or even the PHP-serialized representation or assigning a sequencial number to each case. Those are all valid proposals, but who are we to decide which of these options is the "right" one if the developer deliberately omits the backed value, the one piece of information that tells us, "Look, this is the way this enum is meant to be stored"? The thing is, we can only guess how such a pure enum should be serialized. If you implement a custom DBAL type for your pure enum that takes the name as backed value, you implement a convention for your own enum and that absolutely fine. But we cannot infer a general rule from that. I think, for a library as Doctrine, it is fair to demand that you define your enum as backed if you want it to be stored properly. Besides, I fail to see the difficulty of implementing a backed enum instead of a pure one.
As am I. 🙂 |
This PR was merged into the 4.4 branch. Discussion ---------- [DoctrineBridge] Fix invalid guess with enumType | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | - | License | MIT | Doc PR | - Since doctrine/orm#9304 doctrine allows Enums with the new `enumType` option. ie. ```php #[ORM\Column(type: 'string', enumType: Status::class, length: 1)] public Status $status; ``` The issue is, it break validations at several places: - `doctrinerExtractor` guess it's a `string` (Because of `type = string`), and validation adds the constraint `Assert\Type(string)` - `doctrineLoader` guess it's a string with a maxLength and validation adds a constrain `Assert\Length(max=1)` - Commits ------- 68dd218 Fix invalid guess with enumType
Very good PR ! So, if we're good, a use case could be : enum Suit: string {
case Hearts = 'H';
case Diamonds = 'D';
case Clubs = 'C';
case Spades = 'S';
}
#[Entity]
class Card
{
#[Column(type: 'string', enumType: Suit::class)]
private ?Suit $suit = null;
public function setSuit(Suit $suit): self
{
$this->suit = $suit;
return $this;
}
public function getSuit(): ?Suit
{
return $this->suit;
} Is it okay ? |
I just deleted that sentence from the PHP docs, see php/doc-en#1331 |
When doing it like this, I'm getting this "TypeError":
After deleting the entire Is this the problem that was fixed in symfony/symfony#45012 ? |
Well, there is an easy way to find out, isn't there? If you encounter a problem with enum columns, please open a bug report. |
@ThomasLandauer did you end up starting something? |
@greg0ire This is the first (small) step towards documenting enums - see doctrine#9304 (comment) I started reading the page from the top, and did some minor changes. Biggest change: If you agree, I'd continue adding php attributes to all code blocks.
This is the first (small) step towards documenting enums - see doctrine#9304 (comment) I started reading the page from the top, and did some minor changes. Biggest change: If you agree, I'd continue adding php attributes to all code blocks.
How would be the equivalent of
on xml? I can't find that anywhere on any documentation |
answering myself, (I had other mapping issues and I thought were related to that but not).
equivalent is:
|
ORM based support for enums.
It works based on a new
ReflectionEnumProperty
that wraps around other reflection property types. This avoids the problem of other approaches that hook into DBAL Type abstraction, since that would require some kind of paramterization which is not supported at the moment. In addition it would at the moment require to register each enum explicitly, which this approach does not.This includes support for typed property detection, the previous example could be written as:
As for error handling, when the database returns an invalid enum case, then the regular
ValueError
is not caught thatBackedEnum::from
throws.