phoenix icon indicating copy to clipboard operation
phoenix copied to clipboard

EntityListener can't be applied to multiple services with same class name

Open biozshock opened this issue 5 years ago • 8 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.

biozshock avatar Oct 02 '20 12:10 biozshock

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)

stof avatar Oct 02 '20 12:10 stof

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.

biozshock avatar Oct 02 '20 12:10 biozshock

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

stof avatar Oct 02 '20 13:10 stof

In terms of the symfony bundle that's an ID of the service.

biozshock avatar Oct 02 '20 13:10 biozshock

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.

ostrolucky avatar Oct 02 '20 13:10 ostrolucky

@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.

stof avatar Oct 02 '20 13:10 stof

Marking on hold until contract is changed in ORM

ostrolucky avatar Oct 03 '20 14:10 ostrolucky

Change unblocking this has been merged upstream, so this is ready to implement now

ostrolucky avatar Jan 30 '21 12:01 ostrolucky

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

ostrolucky avatar Jul 21 '23 03:07 ostrolucky