Support fetching entities by aliased name#1181
Conversation
|
Hello, thank you for creating this pull request. I have automatically opened an issue http://www.doctrine-project.org/jira/browse/DDC-3385 We use Jira to track the state of pull requests and the versions they got |
There was a problem hiding this comment.
Not sure why this wasn't there: we depend on the package, therefore it must be in the list.
There was a problem hiding this comment.
This actually returns string, no? If so, the description is also wrong here.
There was a problem hiding this comment.
@beberlei I'd like your input on this one if possible
…r hooking into failed `ClassMetadata` loading
…y resolved `ClassMetadata` as a mutable event result vector
…licate imports (caused by rebase conflicts)
…ssed/cleared
…ewly introduced tests
…nterface-first fetching of metadata (via fallback logic)
963d5cd to
06f256b
Compare
…tadataNotFoundEventArgs`
…undEventArgs` from `ManagerEventArgs` instead of generic `EventArgs`
…etadataNotFoundEventArgs` API
…on to new `OnClassMetadataNotFoundEventArgs` API
…ataNotFoundEventArgs` impl
There was a problem hiding this comment.
This description is wrong, isn't it?
…EventArgs` docblocks as per @deeky666's review
There was a problem hiding this comment.
There is no parent method here as far as I can see. Can you provide a descriptive DocBlock?
There was a problem hiding this comment.
The method is actually an override as per doctrine/common#342
…entities-by-aliased-name Support fetching entities by aliased name
When using the `ResolveTargetEntityListener` to substitute `targetEntities` in association mappings, do not eagerly put the resolved (target) entity into the class metadata cache under the class name of the original entity. #### Motivation I have a library that wants to distribute a MappedSuperclass as the base for some functionality. It will be necessary that clients using the library will extend the MappedSuperclass to fill in some blanks, creating the first real `#[Entity]` instance of it. This client-provided entity will be the primary means of working with the class. Thus, I was following the [note in the documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/reference/inheritance-mapping.html#mapped-superclasses:~:text=It%20is%2C%20however) and using the `ResolveTargetEntityListener` to declare that whenever an association refers to that mapped superclass, the particular entity class shall be used instead. > One-To-Many associations are not generally possible on a mapped superclass, since they require the "many" side to hold the foreign key. > It is, however, possible to use the [ResolveTargetEntityListener](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/cookbook/resolve-target-entity-listener.html) to replace references to a mapped superclass with an entity class at runtime. As long as there is only one entity subclass inheriting from the mapped superclass and all references to the mapped superclass are resolved to that entity class at runtime, the mapped superclass can use One-To-Many associations and be named as the targetEntity on the owning sides. #### Changes made The `ResolveTargetEntityListener` primarily does what its name suggests: For newly loaded class metadata, it inspects all associations declared and replaces the `targetEntity` with new (resolved) values. But additionally, when a loaded class is the target of such a resolution, it would also put the class metadata into the cache under the name of the original entity. I think that extra step is wrong, and this PR removes it. It had the side effect that when other classes extending the MappedSuperclass were loaded _after_ the resolve target class has been seen for the first time, the metadata for those classes would not inherit from the mapped superclass anymore, but from the target entity class instead. In my real-life use case, this causes weird mapping errors down the road; as of ^3.0, it would throw a mapping exception asking to configure inheritance mapping. But note that there would be no inheritance between the two entity classes at all. #### More background The documentation [describes the use of `ResolveTargetEntityListener`](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/cookbook/resolve-target-entity-listener.html) with an interface that is resolved to an entity class. For an interface, adding the extra metadata does not make a difference, since it never interferes with actual entity or mapped superclasses. The initial idea of adding a copy of the entity class metadata under the interface name came from commit 9c7f3f2 in doctrine#385. The goal was to make it possible to also find entities by interface names, like so: ``` $em->find('Foo\BarBundle\Entity\PersonInterface', 1); ``` It then [turned out that this only worked when the resolution had already been applied](doctrine#385 (comment)). So, the new `onClassMetadataNotFound` event was added and the resolution map would be checked in that case as well (doctrine#1181). The inital code stayed in place, possibly giving a small performance gain. In my real-world use case and the test case I added in this PR, the associations are even self-referencing. That should not really be necessary for the problem to surface. I decided to keep it this way to show that the `targetEntity` need not be an interface after all, and that a MappedSuperclass can be used in the same way.
When using the `ResolveTargetEntityListener` to substitute `targetEntities` in association mappings, do not eagerly put the resolved (target) entity into the class metadata cache under the class name of the original entity. #### Motivation I have a library that wants to distribute a MappedSuperclass as the base for some functionality. It will be necessary that clients using the library will extend the MappedSuperclass to fill in some blanks, creating the first real `#[Entity]` instance of it. This client-provided entity will be the primary means of working with the class. Thus, I was following the [note in the documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/reference/inheritance-mapping.html#mapped-superclasses:~:text=It%20is%2C%20however) and using the `ResolveTargetEntityListener` to declare that whenever an association refers to that mapped superclass, the particular entity class shall be used instead. > One-To-Many associations are not generally possible on a mapped superclass, since they require the "many" side to hold the foreign key. > It is, however, possible to use the [ResolveTargetEntityListener](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/cookbook/resolve-target-entity-listener.html) to replace references to a mapped superclass with an entity class at runtime. As long as there is only one entity subclass inheriting from the mapped superclass and all references to the mapped superclass are resolved to that entity class at runtime, the mapped superclass can use One-To-Many associations and be named as the targetEntity on the owning sides. #### Changes made The `ResolveTargetEntityListener` primarily does what its name suggests: For newly loaded class metadata, it inspects all associations declared and replaces the `targetEntity` with new (resolved) values. But additionally, when a loaded class is the target of such a resolution, it would also put the class metadata into the cache under the name of the original entity. I think that extra step is wrong, and this PR removes it. It had the side effect that when other classes extending the MappedSuperclass were loaded _after_ the resolve target class has been seen for the first time, the metadata for those classes would not inherit from the mapped superclass anymore, but from the target entity class instead. In my real-life use case, this causes weird mapping errors down the road; as of ^3.0, it would throw a mapping exception asking to configure inheritance mapping. But note that there would be no inheritance between the two entity classes at all. #### More background The documentation [describes the use of `ResolveTargetEntityListener`](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/cookbook/resolve-target-entity-listener.html) with an interface that is resolved to an entity class. For an interface, adding the extra metadata does not make a difference, since it never interferes with actual entity or mapped superclasses. The initial idea of adding a copy of the entity class metadata under the interface name came from commit 9c7f3f2 in doctrine#385. The goal was to make it possible to also find entities by interface names, like so: ``` $em->find('Foo\BarBundle\Entity\PersonInterface', 1); ``` It then [turned out that this only worked when the resolution had already been applied](doctrine#385 (comment)). So, the new `onClassMetadataNotFound` event was added and the resolution map would be checked in that case as well (doctrine#1181). The inital code stayed in place, possibly giving a small performance gain. In my real-world use case and the test case I added in this PR, the associations are even self-referencing. That should not really be necessary for the problem to surface. I decided to keep it this way to show that the `targetEntity` need not be an interface after all, and that a MappedSuperclass can be used in the same way.
When using the `ResolveTargetEntityListener` to substitute `targetEntities` in association mappings, do not eagerly put the resolved (target) entity into the class metadata cache under the class name of the original entity. #### Motivation I have a library that wants to distribute a MappedSuperclass as the base for some functionality. It will be necessary that clients using the library will extend the MappedSuperclass to fill in some blanks, creating the first real `#[Entity]` instance of it. This client-provided entity will be the primary means of working with the class. Thus, I was following the [note in the documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/reference/inheritance-mapping.html#mapped-superclasses:~:text=It%20is%2C%20however) and using the `ResolveTargetEntityListener` to declare that whenever an association refers to that mapped superclass, the particular entity class shall be used instead. > One-To-Many associations are not generally possible on a mapped superclass, since they require the "many" side to hold the foreign key. > It is, however, possible to use the [ResolveTargetEntityListener](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/cookbook/resolve-target-entity-listener.html) to replace references to a mapped superclass with an entity class at runtime. As long as there is only one entity subclass inheriting from the mapped superclass and all references to the mapped superclass are resolved to that entity class at runtime, the mapped superclass can use One-To-Many associations and be named as the targetEntity on the owning sides. #### Changes made The `ResolveTargetEntityListener` primarily does what its name suggests: For newly loaded class metadata, it inspects all associations declared and replaces the `targetEntity` with new (resolved) values. But additionally, when a loaded class is the target of such a resolution, it would also put the class metadata into the cache under the name of the original entity. I think that extra step is wrong, and this PR removes it. It had the side effect that when other classes extending the MappedSuperclass were loaded _after_ the resolve target class has been seen for the first time, the metadata for those classes would not inherit from the mapped superclass anymore, but from the target entity class instead. In my real-life use case, this causes weird mapping errors down the road; as of ^3.0, it would throw a mapping exception asking to configure inheritance mapping. But note that there would be no inheritance between the two entity classes at all. #### More background The documentation [describes the use of `ResolveTargetEntityListener`](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/cookbook/resolve-target-entity-listener.html) with an interface that is resolved to an entity class. For an interface, adding the extra metadata does not make a difference, since it never interferes with actual entity or mapped superclasses. The initial idea of adding a copy of the entity class metadata under the interface name came from commit 9c7f3f2 in doctrine#385. The goal was to make it possible to also find entities by interface names, like so: ``` $em->find('Foo\BarBundle\Entity\PersonInterface', 1); ``` It then [turned out that this only worked when the resolution had already been applied](doctrine#385 (comment)). So, the new `onClassMetadataNotFound` event was added and the resolution map would be checked in that case as well (doctrine#1181). The inital code stayed in place, possibly giving a small performance gain. In my real-world use case and the test case I added in this PR, the associations are even self-referencing. That should not really be necessary for the problem to surface. I decided to keep it this way to show that the `targetEntity` need not be an interface after all, and that a MappedSuperclass can be used in the same way.
When using the `ResolveTargetEntityListener` to substitute `targetEntities` in association mappings, do not eagerly put the resolved (target) entity into the class metadata cache under the class name of the original entity. #### Motivation I have a library that wants to distribute a MappedSuperclass as the base for some functionality. It will be necessary that clients using the library will extend the MappedSuperclass to fill in some blanks, creating the first real `#[Entity]` instance of it. This client-provided entity will be the primary means of working with the class. Thus, I was following the [note in the documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/reference/inheritance-mapping.html#mapped-superclasses:~:text=It%20is%2C%20however) and using the `ResolveTargetEntityListener` to declare that whenever an association refers to that mapped superclass, the particular entity class shall be used instead. > One-To-Many associations are not generally possible on a mapped superclass, since they require the "many" side to hold the foreign key. > It is, however, possible to use the [ResolveTargetEntityListener](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/cookbook/resolve-target-entity-listener.html) to replace references to a mapped superclass with an entity class at runtime. As long as there is only one entity subclass inheriting from the mapped superclass and all references to the mapped superclass are resolved to that entity class at runtime, the mapped superclass can use One-To-Many associations and be named as the targetEntity on the owning sides. #### Changes made The `ResolveTargetEntityListener` primarily does what its name suggests: For newly loaded class metadata, it inspects all associations declared and replaces the `targetEntity` with new (resolved) values. But additionally, when a loaded class is the target of such a resolution, it would also put the class metadata into the cache under the name of the original entity. I think that extra step is wrong, and this PR removes it. It had the side effect that when other classes extending the MappedSuperclass were loaded _after_ the resolve target class has been seen for the first time, the metadata for those classes would not inherit from the mapped superclass anymore, but from the target entity class instead. In my real-life use case, this causes weird mapping errors down the road; as of ^3.0, it would throw a mapping exception asking to configure inheritance mapping. But note that there would be no inheritance between the two entity classes at all. #### More background The documentation [describes the use of `ResolveTargetEntityListener`](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/cookbook/resolve-target-entity-listener.html) with an interface that is resolved to an entity class. For an interface, adding the extra metadata does not make a difference, since it never interferes with actual entity or mapped superclasses. The initial idea of adding a copy of the entity class metadata under the interface name came from commit 9c7f3f2 in doctrine#385. The goal was to make it possible to also find entities by interface names, like so: ``` $em->find('Foo\BarBundle\Entity\PersonInterface', 1); ``` It then [turned out that this only worked when the resolution had already been applied](doctrine#385 (comment)). So, the new `onClassMetadataNotFound` event was added and the resolution map would be checked in that case as well (doctrine#1181). The inital code stayed in place, possibly giving a small performance gain. In my real-world use case and the test case I added in this PR, the associations are even self-referencing. That should not really be necessary for the problem to surface. I decided to keep it this way to show that the `targetEntity` need not be an interface after all, and that a MappedSuperclass can be used in the same way.
When using the `ResolveTargetEntityListener` to substitute `targetEntities` in association mappings, do not eagerly put the resolved (target) entity into the class metadata cache under the class name of the original entity. #### Motivation I have a library that wants to distribute a MappedSuperclass as the base for some functionality. It will be necessary that clients using the library will extend the MappedSuperclass to fill in some blanks, creating the first real `#[Entity]` instance of it. This client-provided entity will be the primary means of working with the class. Thus, I was following the [note in the documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/reference/inheritance-mapping.html#mapped-superclasses:~:text=It%20is%2C%20however) and using the `ResolveTargetEntityListener` to declare that whenever an association refers to that mapped superclass, the particular entity class shall be used instead. > One-To-Many associations are not generally possible on a mapped superclass, since they require the "many" side to hold the foreign key. > It is, however, possible to use the [ResolveTargetEntityListener](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/cookbook/resolve-target-entity-listener.html) to replace references to a mapped superclass with an entity class at runtime. As long as there is only one entity subclass inheriting from the mapped superclass and all references to the mapped superclass are resolved to that entity class at runtime, the mapped superclass can use One-To-Many associations and be named as the targetEntity on the owning sides. #### Changes made The `ResolveTargetEntityListener` primarily does what its name suggests: For newly loaded class metadata, it inspects all associations declared and replaces the `targetEntity` with new (resolved) values. But additionally, when a loaded class is the target of such a resolution, it would also put the class metadata into the cache under the name of the original entity. I think that extra step is wrong, and this PR removes it. It had the side effect that when other classes extending the MappedSuperclass were loaded _after_ the resolve target class has been seen for the first time, the metadata for those classes would not inherit from the mapped superclass anymore, but from the target entity class instead. In my real-life use case, this causes weird mapping errors down the road; as of ^3.0, it would throw a mapping exception asking to configure inheritance mapping. But note that there would be no inheritance between the two entity classes at all. #### More background The documentation [describes the use of `ResolveTargetEntityListener`](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/cookbook/resolve-target-entity-listener.html) with an interface that is resolved to an entity class. For an interface, adding the extra metadata does not make a difference, since it never interferes with actual entity or mapped superclasses. The initial idea of adding a copy of the entity class metadata under the interface name came from commit 9c7f3f2 in doctrine#385. The goal was to make it possible to also find entities by interface names, like so: ``` $em->find('Foo\BarBundle\Entity\PersonInterface', 1); ``` It then [turned out that this only worked when the resolution had already been applied](doctrine#385 (comment)). So, the new `onClassMetadataNotFound` event was added and the resolution map would be checked in that case as well (doctrine#1181). The inital code stayed in place, possibly giving a small performance gain. In my real-world use case and the test case I added in this PR, the associations are even self-referencing. That should not really be necessary for the problem to surface. I decided to keep it this way to show that the `targetEntity` need not be an interface after all, and that a MappedSuperclass can be used in the same way.
* Do not eagerly set metadata from ResolveTargetEntityListener When using the `ResolveTargetEntityListener` to substitute `targetEntities` in association mappings, do not eagerly put the resolved (target) entity into the class metadata cache under the class name of the original entity. #### Motivation I have a library that wants to distribute a MappedSuperclass as the base for some functionality. It will be necessary that clients using the library will extend the MappedSuperclass to fill in some blanks, creating the first real `#[Entity]` instance of it. This client-provided entity will be the primary means of working with the class. Thus, I was following the [note in the documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/reference/inheritance-mapping.html#mapped-superclasses:~:text=It%20is%2C%20however) and using the `ResolveTargetEntityListener` to declare that whenever an association refers to that mapped superclass, the particular entity class shall be used instead. > One-To-Many associations are not generally possible on a mapped superclass, since they require the "many" side to hold the foreign key. > It is, however, possible to use the [ResolveTargetEntityListener](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/cookbook/resolve-target-entity-listener.html) to replace references to a mapped superclass with an entity class at runtime. As long as there is only one entity subclass inheriting from the mapped superclass and all references to the mapped superclass are resolved to that entity class at runtime, the mapped superclass can use One-To-Many associations and be named as the targetEntity on the owning sides. #### Changes made The `ResolveTargetEntityListener` primarily does what its name suggests: For newly loaded class metadata, it inspects all associations declared and replaces the `targetEntity` with new (resolved) values. But additionally, when a loaded class is the target of such a resolution, it would also put the class metadata into the cache under the name of the original entity. I think that extra step is wrong, and this PR removes it. It had the side effect that when other classes extending the MappedSuperclass were loaded _after_ the resolve target class has been seen for the first time, the metadata for those classes would not inherit from the mapped superclass anymore, but from the target entity class instead. In my real-life use case, this causes weird mapping errors down the road; as of ^3.0, it would throw a mapping exception asking to configure inheritance mapping. But note that there would be no inheritance between the two entity classes at all. #### More background The documentation [describes the use of `ResolveTargetEntityListener`](https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/cookbook/resolve-target-entity-listener.html) with an interface that is resolved to an entity class. For an interface, adding the extra metadata does not make a difference, since it never interferes with actual entity or mapped superclasses. The initial idea of adding a copy of the entity class metadata under the interface name came from commit 9c7f3f2 in #385. The goal was to make it possible to also find entities by interface names, like so: ``` $em->find('Foo\BarBundle\Entity\PersonInterface', 1); ``` It then [turned out that this only worked when the resolution had already been applied](#385 (comment)). So, the new `onClassMetadataNotFound` event was added and the resolution map would be checked in that case as well (#1181). The inital code stayed in place, possibly giving a small performance gain. In my real-world use case and the test case I added in this PR, the associations are even self-referencing. That should not really be necessary for the problem to surface. I decided to keep it this way to show that the `targetEntity` need not be an interface after all, and that a MappedSuperclass can be used in the same way.
See #385 (this PR supersedes it)
The final aim is allowing something like:
This PR depends also on doctrine/common#342