diff --git a/DependencyInjection/Compiler/EntityListenerPass.php b/DependencyInjection/Compiler/EntityListenerPass.php
index 02abda421..124f2eb7c 100644
--- a/DependencyInjection/Compiler/EntityListenerPass.php
+++ b/DependencyInjection/Compiler/EntityListenerPass.php
@@ -4,6 +4,7 @@
use Doctrine\Bundle\DoctrineBundle\Mapping\ContainerEntityListenerResolver;
use Doctrine\Bundle\DoctrineBundle\Mapping\EntityListenerServiceResolver;
+use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
@@ -44,10 +45,10 @@ public function process(ContainerBuilder $container)
$resolver->setPublic(true);
if (isset($attributes['entity']) && isset($attributes['event'])) {
- $this->attachToListener($container, $name, $id, $attributes);
+ $this->attachToListener($container, $name, $this->getConcreteDefinitionClass($container->findDefinition($id), $container, $id), $attributes);
}
- $resolverClass = $this->getResolverClass($resolver, $container);
+ $resolverClass = $this->getResolverClass($resolver, $container, $resolverId);
$resolverSupportsLazyListeners = is_a($resolverClass, EntityListenerServiceResolver::class, true);
$lazyByAttribute = isset($attributes['lazy']) && $attributes['lazy'];
@@ -65,7 +66,7 @@ public function process(ContainerBuilder $container)
throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as this entity listener is lazy-loaded.', $id));
}
- $resolver->addMethodCall('registerService', [$listener->getClass(), $id]);
+ $resolver->addMethodCall('registerService', [$this->getConcreteDefinitionClass($listener, $container, $id), $id]);
// if the resolver uses the default class we will use a service locator for all listeners
if ($resolverClass === ContainerEntityListenerResolver::class) {
@@ -87,7 +88,7 @@ public function process(ContainerBuilder $container)
}
}
- private function attachToListener(ContainerBuilder $container, $name, $id, array $attributes)
+ private function attachToListener(ContainerBuilder $container, $name, string $class, array $attributes)
{
$listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $name);
@@ -95,32 +96,49 @@ private function attachToListener(ContainerBuilder $container, $name, $id, array
return;
}
- $serviceDef = $container->getDefinition($id);
-
$args = [
$attributes['entity'],
- $serviceDef->getClass(),
+ $class,
$attributes['event'],
];
if (isset($attributes['method'])) {
$args[] = $attributes['method'];
- } elseif (! method_exists($serviceDef->getClass(), $attributes['event']) && method_exists($serviceDef->getClass(), '__invoke')) {
+ } elseif (! method_exists($class, $attributes['event']) && method_exists($class, '__invoke')) {
$args[] = '__invoke';
}
$container->findDefinition($listenerId)->addMethodCall('addEntityListener', $args);
}
- private function getResolverClass(Definition $resolver, ContainerBuilder $container) : string
+ private function getResolverClass(Definition $resolver, ContainerBuilder $container, string $id) : string
{
- $resolverClass = $resolver->getClass();
+ $resolverClass = $this->getConcreteDefinitionClass($resolver, $container, $id);
if (substr($resolverClass, 0, 1) === '%') {
// resolve container parameter first
- $resolverClass = $container->getParameterBag()->resolveValue($resolver->getClass());
+ $resolverClass = $container->getParameterBag()->resolveValue($resolverClass);
}
return $resolverClass;
}
+
+ private function getConcreteDefinitionClass(Definition $definition, ContainerBuilder $container, string $id) : string
+ {
+ $class = $definition->getClass();
+ if ($class) {
+ return $class;
+ }
+
+ while ($definition instanceof ChildDefinition) {
+ $definition = $container->findDefinition($definition->getParent());
+
+ $class = $definition->getClass();
+ if ($class) {
+ return $class;
+ }
+ }
+
+ throw new InvalidArgumentException(sprintf('The service "%s" must define its class.', $id));
+ }
}
diff --git a/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php b/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php
index bab4a8aa0..0d6153d14 100644
--- a/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php
+++ b/Tests/DependencyInjection/AbstractDoctrineExtensionTest.php
@@ -860,7 +860,8 @@ public function testAttachEntityListenerTag()
['EntityListener1', 'entity_listener1'],
['Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Fixtures\InvokableEntityListener', 'invokable_entity_listener'],
['Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Fixtures\InvokableEntityListener', 'invokable_entity_listener'],
- ], 3);
+ ['ParentEntityListener', 'children_entity_listener'],
+ ], 4);
$listener = $container->getDefinition('doctrine.orm.em2_entity_listener_resolver');
$this->assertDICDefinitionMethodCallOnce($listener, 'registerService', ['EntityListener2', 'entity_listener2']);
@@ -869,6 +870,7 @@ public function testAttachEntityListenerTag()
$this->assertDICDefinitionMethodCallAt(0, $attachListener, 'addEntityListener', ['My/Entity1', 'EntityListener1', 'postLoad']);
$this->assertDICDefinitionMethodCallAt(1, $attachListener, 'addEntityListener', ['My/Entity1', 'Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Fixtures\InvokableEntityListener', 'loadClassMetadata', '__invoke']);
$this->assertDICDefinitionMethodCallAt(2, $attachListener, 'addEntityListener', ['My/Entity1', 'Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Fixtures\InvokableEntityListener', 'postPersist']);
+ $this->assertDICDefinitionMethodCallAt(3, $attachListener, 'addEntityListener', ['My/Entity3', 'ParentEntityListener', 'postLoad']);
$attachListener = $container->getDefinition('doctrine.orm.em2_listeners.attach_entity_listeners');
$this->assertDICDefinitionMethodCallOnce($attachListener, 'addEntityListener', ['My/Entity2', 'EntityListener2', 'preFlush', 'preFlushHandler']);
diff --git a/Tests/DependencyInjection/Fixtures/config/xml/orm_attach_entity_listener_tag.xml b/Tests/DependencyInjection/Fixtures/config/xml/orm_attach_entity_listener_tag.xml
index e8fd2a2b4..16fc5cc5d 100644
--- a/Tests/DependencyInjection/Fixtures/config/xml/orm_attach_entity_listener_tag.xml
+++ b/Tests/DependencyInjection/Fixtures/config/xml/orm_attach_entity_listener_tag.xml
@@ -19,6 +19,14 @@
+
+
+
+
+
+
+
+
diff --git a/Tests/DependencyInjection/Fixtures/config/yml/orm_attach_entity_listener_tag.yml b/Tests/DependencyInjection/Fixtures/config/yml/orm_attach_entity_listener_tag.yml
index 346ee27f6..d3d4b5197 100644
--- a/Tests/DependencyInjection/Fixtures/config/yml/orm_attach_entity_listener_tag.yml
+++ b/Tests/DependencyInjection/Fixtures/config/yml/orm_attach_entity_listener_tag.yml
@@ -2,12 +2,12 @@ services:
entity_listener1:
class: EntityListener1
tags:
- - { name: doctrine.orm.entity_listener, entity: My/Entity1, event: postLoad}
+ - { name: doctrine.orm.entity_listener, entity: My/Entity1, event: postLoad }
entity_listener2:
class: EntityListener2
tags:
- - { name: doctrine.orm.entity_listener, entity_manager: em2, entity: My/Entity2, event: preFlush, method: preFlushHandler}
+ - { name: doctrine.orm.entity_listener, entity_manager: em2, entity: My/Entity2, event: preFlush, method: preFlushHandler }
invokable_entity_listener:
class: Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Fixtures\InvokableEntityListener
@@ -15,6 +15,18 @@ services:
- { name: doctrine.orm.entity_listener, entity: My/Entity1, event: loadClassMetadata }
- { name: doctrine.orm.entity_listener, entity: My/Entity1, event: postPersist }
+ grand_parent_entity_listener:
+ class: GrandParentEntityListener
+
+ parent_entity_listener:
+ parent: grand_parent_entity_listener
+ class: ParentEntityListener
+
+ children_entity_listener:
+ parent: parent_entity_listener
+ tags:
+ - { name: doctrine.orm.entity_listener, entity: My/Entity3, event: postLoad }
+
doctrine:
dbal:
default_connection: default