-
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
Use typed properties for default metadata for #7939 #8439
Conversation
I don't know how to proceed with tests under PHP < 7.4 as there are no typed properties and entity class required for new test cases fails there. I probably could move it to other namespace, but that seems like complicating file structure without much benefit. Please help me with any suggestion :) |
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.
Thank you for your PR, this is a very good first contribution, congrats :-)
I made some comments and have some questions inline.
@@ -1414,6 +1420,53 @@ protected function _validateAndCompleteFieldMapping(array &$mapping) | |||
throw MappingException::missingFieldName($this->name); | |||
} | |||
|
|||
if ( | |||
PHP_VERSION_ID >= 70400 | |||
&& isset($this->reflClass) |
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.
Just to understand, do you have a case where $this->reflClass
is not set? I assume when DisconnectedMetadataFactory
is used while converting an XML/YML mapping or generating entities?
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 condition is duplicated below as well, can you factor it out into a private method?
private function isTypedProperty($name) : bool
{
}
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 I remove isset($this->reflClass)
there are 56 test errors and 3 failures with Error: Call to a member function hasProperty() on null
message. I have no idea if those tests should be fixed or it is expected behavior and don't know Doctrine internals enough yet to determine if it can occur in real usage.
I can work through those tests and enforce setting $this->reflClass
if that's desired, but someone more experience would need to take a look for real cases where unset reflClass
is desired.
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 have also factored condition as requested
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 makes sense that it fails for when the DisconnectedMetadataFactory
is used in case of EntityGenerator and exporter and such things. Of course this creates an inconsistency where the types default to "string" when reflClass does not exist. But there is no way to avoid that at the moment, so lets keep it this way.
case DateTimeImmutable::class: | ||
$mapping['type'] = 'datetime_immutable'; | ||
break; | ||
case 'array': |
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 is ambiguous, could also be simple_array
or object
type. Not sure what to do, since otherwise it will default to "string"
later.
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.
My reasoning was to provide useful defaults instead of solving every possible use case. In this example having "string"
as property type would simply fail at runtime as ORM would try to assign string
value to array
typed property. Therefore I believe useful default is better than nothing.
But the same problem comes with \DateType
and many other fields. I believe the best long-term solution would be to create new interface, for example DefaultTypeResolverInterface
that could look like this:
interface DefaultTypeResolverInterface
{
public function determineTypeForProperty(\ReflectionType $type): ?string;
}
And provide at least one implementation (maybe working as here) and then allow users to choose implementation based on their config.
This way we could provide useful defaults for everyone and allow users to have simple method of providing own rules (for example I would benefit from overwriting \DateTimeImmutable
in my projects).
I don't know how to approach such solution in ORM structure, but would love to do so. If this seems reasonable please just give me some directions and I will follow through.
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, lets stay with the hardcoded resolving rules for now and we'll leave the replacable resolving rules for a future PR.
You should document what PHP types get mapped to which Doctrine types in the documentation, see docs/
folder.
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.
OK, I have added documentation where it seemed logical to me
Thanks for the review. I will work on it next week 👍 |
I have rebased onto 2.9.x (still some conflicts pop out, don't know why) and handled suggestions 👍 |
* | ||
* @return void | ||
*/ | ||
private function _validateAndCompleteTypedFieldMapping(array &$mapping) |
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.
don't pass by reference please but return the array again. This is how other _validate
methods in this class do it already, so that stay consistent.
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.
OK, I have changed _validateAndCompleteFieldMapping too while at it - it was still using reference.
* | ||
* @return void | ||
*/ | ||
private function _validateAndCompleteTypedAssociationMapping(array &$mapping) |
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.
same here, return the array again and don't pass by reference
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.
OK, done
I think I addressed all current comments to some degree. I see that checks are somehow stuck and I see no way to "unstuck" them to see if my code passes. Is it possible somehow to run them? |
@Lustmored this is because of the conflict in |
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.
Just two small changes, then I believe this is ready to go.
@@ -715,6 +717,7 @@ Required attributes: | |||
|
|||
- **targetEntity**: FQCN of the referenced target entity. Can be the | |||
unqualified class name if both classes are in the same namespace. | |||
When typed properties are used it is inherited from PHP 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.
I would formulate this a bit differently: You can omit this value if you use a PHP property type instead.
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.
OK, changed
|
||
// Join table Nullable | ||
$cm->mapOneToOne(['fieldName' => 'email', 'joinColumns' => [[]]]); | ||
$this->assertFalse($cm->getAssociationMapping('email')['joinColumns'][0]['nullable']); |
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.
Missing assertion on target_entity
.
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.
Right, missed it. Assertion added
I think I have complied with all your comments. I have also rebased onto 2.9.x and pushed CS fixed. Only remaining failing check is CS complaing about method names starting with underscore - I don't have the confidence to change them without asking first. |
I have rebased onto 2.9 and made a simple change to use |
@Lustmored thank you very much! |
@Lustmored FYI, This doesn't work anymore with AttributeDriver (does it with Annotations?) Because of the default value of |
@beberlei I see the problem and I see how I missed it in tests (I have created only tests for |
@Lustmored i think setting Column::$type to nullable & default null is better |
@beberlei That's exactly what I'm experimenting with right now. Probably will come with a PR fixing the issue and introducing more tests to ensure it works with mapping later today 👍 |
This is simple implementation of idea outlined in #7939. During completion step of a field mapping completion or association mapping completion it looks for typed property and, when existent, uses it to determine default for
type
,targetEntity
andnullable
(in fields andjoinTable
).I have added simple tests covering all cases I found.
I am not a great fan of encapsulating code in
PHP_VERSION_ID >= 70400
, but at the time of writing got no better idea.I couldn't get master branch to work locally and that's the only reason why I target '2.9.x' (Composer has problem resolving doctrine/dbal).
This is my first PR to Doctrine project, so if anything is done wrong please let me know and I will do my best to fix it :)