diff --git a/src/DependencyInjection/Compiler/EntityListenerPass.php b/src/DependencyInjection/Compiler/EntityListenerPass.php index 23692e18..0d55fbc4 100644 --- a/src/DependencyInjection/Compiler/EntityListenerPass.php +++ b/src/DependencyInjection/Compiler/EntityListenerPass.php @@ -6,7 +6,6 @@ use Doctrine\Bundle\DoctrineBundle\Mapping\EntityListenerServiceResolver; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -17,6 +16,7 @@ use function method_exists; use function sprintf; use function substr; +use function usort; /** * Class for Symfony bundles to register entity listeners @@ -25,18 +25,15 @@ */ class EntityListenerPass implements CompilerPassInterface { - use PriorityTaggedServiceTrait; - /** @return void */ public function process(ContainerBuilder $container) { - $resolvers = $this->findAndSortTaggedServices('doctrine.orm.entity_listener', $container); - $lazyServiceReferencesByResolver = []; - foreach ($resolvers as $reference) { - $id = $reference->__toString(); - foreach ($container->getDefinition($id)->getTag('doctrine.orm.entity_listener') as $attributes) { + foreach ($container->findTaggedServiceIds('doctrine.orm.entity_listener') as $id => $tags) { + usort($tags, static fn (array $a, array $b) => ($a['priority'] ?? 0) <=> ($b['priority'] ?? 0)); + + foreach ($tags as $attributes) { $name = $attributes['entity_manager'] ?? $container->getParameter('doctrine.default_entity_manager'); $entityManager = sprintf('doctrine.orm.%s_entity_manager', $name); diff --git a/tests/DependencyInjection/Compiler/EntityListenerPassTest.php b/tests/DependencyInjection/Compiler/EntityListenerPassTest.php index 0f9d1890..d8a0ad2c 100644 --- a/tests/DependencyInjection/Compiler/EntityListenerPassTest.php +++ b/tests/DependencyInjection/Compiler/EntityListenerPassTest.php @@ -64,6 +64,43 @@ public function provideEvents(): iterable yield 'With event and custom method' => [Events::postLoad, 'postLoadHandler', 'postLoadHandler']; yield 'With event and no matching method' => [Events::postLoad, null, '__invoke']; } + + public function testMultipleAttributesOnSameClassKeepTheCorrectOrder(): void + { + $container = new ContainerBuilder(); + $container->addCompilerPass(new EntityListenerPass()); + + $container->setParameter('doctrine.default_entity_manager', 'default'); + $container->register('doctrine.orm.default_entity_manager', EntityManager::class); + $container->register('doctrine.orm.default_entity_listener_resolver', ContainerEntityListenerResolver::class); + $container->register('doctrine.orm.default_listeners.attach_entity_listeners', AttachEntityListenersListener::class) + ->setPublic(true); + + $container->register(TestListener::class) + ->addTag('doctrine.orm.entity_listener', ['entity' => stdClass::class, 'event' => Events::prePersist]) + ->addTag('doctrine.orm.entity_listener', ['entity' => stdClass::class, 'event' => Events::postPersist]); + $container->register(TestListener2::class) + ->addTag( + 'doctrine.orm.entity_listener', + ['entity' => stdClass::class, 'event' => Events::prePersist, 'priority' => 1], + ) + ->addTag( + 'doctrine.orm.entity_listener', + ['entity' => stdClass::class, 'event' => Events::postPersist, 'priority' => -1], + ); + + $container->compile(); + + $this->assertSame( + [ + ['addEntityListener', ['stdClass', TestListener::class, 'prePersist']], + ['addEntityListener', ['stdClass', TestListener::class, 'postPersist']], + ['addEntityListener', ['stdClass', TestListener2::class, 'postPersist']], + ['addEntityListener', ['stdClass', TestListener2::class, 'prePersist']], + ], + $container->getDefinition('doctrine.orm.default_listeners.attach_entity_listeners')->getMethodCalls(), + ); + } } class TestListener @@ -84,3 +121,15 @@ public function __invoke(): void { } } + + +class TestListener2 +{ + public function prePersist(): void + { + } + + public function postPersist(): void + { + } +}