EntityListener can't be applied to multiple services with same class name
biozshock opened this issue · 9 comments
With current implementation of ContainerEntityListenerResolver
it's not possible to use same class, even if it has different service IDs
Consider following config:
services:
service_a:
class: My\Class
parameters: ['param1`]
tags:
- {'name': 'doctrine.orm.entity_listener', 'entity': 'My\Entity1'}
service_b:
class: My\Class
parameters: ['param2`]
tags:
- {'name': 'doctrine.orm.entity_listener', 'entity': 'My\Entity2'}
The entity listener service_b
will be applied to entity My\Entity1
, because https://github.com/doctrine/DoctrineBundle/blob/2.1.2/Mapping/ContainerEntityListenerResolver.php#L66 uses class name instead of the service ID to collect entity listeners.
AFAICT, this is because the way the ORM works is that it uses a class name as the input of the resolver (and so does not allow having multiple ones with the same class name)
This is because how a class metadata is collected in EntityListenerPass
. Doctrine does not operate with class names here, but with strings. I'm positive that DoctrineBundle can put service IDs instead of real class names, and it will just work.
Though i'm not really sure how to implement such logic for services that are marked as lazy
. Because an interface accepts only an instance of the listener, w/o the ID.
The interface in the ORM explicitly declares the argument as being a class name rather than an arbitrary string: https://github.com/doctrine/orm/blob/e0eb82a3b12a3fd0878b5d56c3c293c04c168329/lib/Doctrine/ORM/Mapping/EntityListenerResolver.php#L42-L51
In terms of the symfony bundle that's an ID of the service.
Not in your example. If you want to discrespect the contract, you can do it on your own, but we don't want to do that in a bundle. Another option is to open issue in ORM which relaxes the contract.
@biozshock no. The ContainerEntityListenerResolver lookups the provided argument into the ServiceLocator it takes as argument. So it can indeed work with any kind of string internally as long as both sides are matching. But the expectation of the signature are defined by the interface it implements, which is why the ServiceLocator is configured with class names as keys.
Marking on hold until contract is changed in ORM
Change unblocking this has been merged upstream, so this is ready to implement now
Hmm I think we still can't do anything here, because resolve method must return 1 object (listener) only. Unfortunately you will have to ask in https://github.com/doctrine/orm to change its event system