Skip to content

Commit

Permalink
Enable attaching an entity listener without specifying an event
Browse files Browse the repository at this point in the history
  • Loading branch information
HypeMC authored and ostrolucky committed Oct 16, 2022
1 parent e7201bb commit e781f9c
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 4 deletions.
8 changes: 4 additions & 4 deletions DependencyInjection/Compiler/EntityListenerPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public function process(ContainerBuilder $container)
$resolver = $container->findDefinition($resolverId);
$resolver->setPublic(true);

if (isset($attributes['entity']) && isset($attributes['event'])) {
if (isset($attributes['entity'])) {
$this->attachToListener($container, $name, $this->getConcreteDefinitionClass($container->findDefinition($id), $container, $id), $attributes);
}

Expand Down Expand Up @@ -94,7 +94,7 @@ public function process(ContainerBuilder $container)
}
}

/** @param array{entity: class-string, event: string} $attributes */
/** @param array{entity: class-string, event?: ?string} $attributes */
private function attachToListener(ContainerBuilder $container, string $name, string $class, array $attributes): void
{
$listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $name);
Expand All @@ -106,12 +106,12 @@ private function attachToListener(ContainerBuilder $container, string $name, str
$args = [
$attributes['entity'],
$class,
$attributes['event'],
$attributes['event'] ?? null,
];

if (isset($attributes['method'])) {
$args[] = $attributes['method'];
} elseif (! method_exists($class, $attributes['event']) && method_exists($class, '__invoke')) {
} elseif (isset($attributes['event']) && ! method_exists($class, $attributes['event']) && method_exists($class, '__invoke')) {
$args[] = '__invoke';
}

Expand Down
86 changes: 86 additions & 0 deletions Tests/DependencyInjection/Compiler/EntityListenerPassTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

namespace Doctrine\Bundle\DoctrineBundle\Tests\DependencyInjection\Compiler;

use Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler\EntityListenerPass;
use Doctrine\Bundle\DoctrineBundle\Mapping\ContainerEntityListenerResolver;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Events;
use Doctrine\ORM\Tools\AttachEntityListenersListener;
use PHPUnit\Framework\TestCase;
use stdClass;
use Symfony\Component\DependencyInjection\ContainerBuilder;

use function interface_exists;

class EntityListenerPassTest extends TestCase
{
public static function setUpBeforeClass(): void
{
if (interface_exists(EntityManagerInterface::class)) {
return;
}

self::markTestSkipped('This test requires ORM');
}

/** @dataProvider provideEvents */
public function testEntityListenersAreRegistered(?string $event, ?string $method, ?string $expectedMethod): 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);

$tagAttributes = [
'entity' => stdClass::class,
'event' => $event,
'method' => $method,
];
$container->register(TestListener::class)->addTag('doctrine.orm.entity_listener', $tagAttributes);

$container->compile();

$definition = $container->getDefinition('doctrine.orm.default_listeners.attach_entity_listeners');

$methodCalls = $definition->getMethodCalls();
self::assertSame('addEntityListener', $methodCalls[0][0]);
self::assertSame(stdClass::class, $methodCalls[0][1][0]);
self::assertSame(TestListener::class, $methodCalls[0][1][1]);
self::assertSame($event, $methodCalls[0][1][2]);
self::assertSame($expectedMethod, $methodCalls[0][1][3] ?? null);
}

/** @return iterable<array{0: ?string, 1: ?string, 2: ?string}> */
public function provideEvents(): iterable
{
yield 'With event and matching method' => [Events::prePersist, null, null];
yield 'Without event' => [null, null, null];
yield 'With event and custom method' => [Events::postLoad, 'postLoadHandler', 'postLoadHandler'];
yield 'With event and no matching method' => [Events::postLoad, null, '__invoke'];
}
}

class TestListener
{
public function prePersist(): void
{
}

public function postPersist(): void
{
}

public function postLoadHandler(): void
{
}

public function __invoke(): void
{
}
}

0 comments on commit e781f9c

Please sign in to comment.