From c12eae925b356ab7f6a001b6efa5c9562611c8ce Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 10 Apr 2021 11:38:41 +0200 Subject: [PATCH] Added ability to register ORM listeners --- CHANGELOG.md | 1 + README.md | 6 +++ src/Doctrine/EntityManagerFactory.php | 18 ++++++- test/Doctrine/EntityManagerFactoryTest.php | 55 ++++++++++++++++++---- 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87e81cf..dd4cb53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this ## [Unreleased] ### Added * [#70](https://github.com/shlinkio/shlink-common/issues/70) Added support for `symfony/mercure` 0.5. +* [#72](https://github.com/shlinkio/shlink-common/issues/72) Added ability to register event listeners in the EntityManager through the `EntityManagerFactory`. ### Changed * *Nothing* diff --git a/README.md b/README.md index f2ea3fd..5dd5db8 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,8 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Common; +use Doctrine\ORM\Events; + return [ 'entity_manager' => [ @@ -125,6 +127,10 @@ return [ Doctrine\Type\ChronosDateTimeType::CHRONOS_DATETIME => Doctrine\Type\ChronosDateTimeType::class, ], 'load_mappings_using_functional_style' => true, // Makes loader assume mappings return a function which should be invoked. Defaults to false + 'listeners' => [ // Map telling which service listeners to invoke for every ORM event + Events::postFlush => ['some_service'], + Events::preUpdate => ['foo', 'bar'], + ] ], 'connection' => [ // Database connection params 'driver' => 'pdo_mysql', diff --git a/src/Doctrine/EntityManagerFactory.php b/src/Doctrine/EntityManagerFactory.php index 2787577..3c71813 100644 --- a/src/Doctrine/EntityManagerFactory.php +++ b/src/Doctrine/EntityManagerFactory.php @@ -42,7 +42,11 @@ public function __invoke(ContainerInterface $container): EntityManager $config->setDefaultRepositoryClassName($defaultRepo); } - return EntityManager::create($connectionConfig, $config); + $em = EntityManager::create($connectionConfig, $config); + + $this->registerListeners($ormConfig, $em, $container); + + return $em; } /** @@ -58,4 +62,16 @@ private function registerTypes(array $ormConfig): void } } } + + private function registerListeners(array $ormConfig, EntityManager $em, ContainerInterface $container): void + { + $listeners = $ormConfig['listeners'] ?? []; + $events = $em->getEventManager(); + + foreach ($listeners as $event => $services) { + foreach ($services as $service) { + $events->addEventListener($event, $container->get($service)); + } + } + } } diff --git a/test/Doctrine/EntityManagerFactoryTest.php b/test/Doctrine/EntityManagerFactoryTest.php index e9a63cd..023cdce 100644 --- a/test/Doctrine/EntityManagerFactoryTest.php +++ b/test/Doctrine/EntityManagerFactoryTest.php @@ -7,6 +7,7 @@ use Doctrine\DBAL\Driver\PDO\SQLite\Driver as SQLiteDriver; use Doctrine\DBAL\Types\Type; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Events; use Doctrine\Persistence\Mapping\Driver\PHPDriver; use Laminas\ServiceManager\ServiceManager; use PHPUnit\Framework\TestCase; @@ -14,10 +15,12 @@ use Shlinkio\Shlink\Common\Doctrine\EntityManagerFactory; use Shlinkio\Shlink\Common\Doctrine\Type\ChronosDateTimeType; use ShlinkioTest\Shlink\Common\Repository\CustomRepository; +use stdClass; use function array_filter; use function array_merge; use function array_merge_recursive; +use function count; use const ARRAY_FILTER_USE_KEY; @@ -47,10 +50,17 @@ public function setUp(): void * @test * @dataProvider provideConfig */ - public function serviceIsCreated(array $config, int $expectedAutoGenerateProxies, string $expectedDefaultRepo): void - { + public function serviceIsCreated( + array $config, + int $expectedAutoGenerateProxies, + string $expectedDefaultRepo, + int $expectedListeners + ): void { $sm = new ServiceManager(['services' => [ 'config' => $config, + 'foo_listener' => new stdClass(), + 'bar_listener' => new stdClass(), + 'baz_listener' => new stdClass(), ]]); self::assertFalse(Type::hasType(ChronosDateTimeType::CHRONOS_DATETIME)); @@ -65,6 +75,19 @@ public function serviceIsCreated(array $config, int $expectedAutoGenerateProxies /** @var PHPDriver $metaDriver */ $metaDriver = $em->getConfiguration()->getMetadataDriverImpl(); self::assertEquals([__FILE__], $metaDriver->getLocator()->getPaths()); + + $events = $em->getEventManager(); + $ref = new ReflectionObject($events); + $prop = $ref->getProperty('_listeners'); + $prop->setAccessible(true); + $listeners = $prop->getValue($events); + + $listenersCount = 0; + foreach ($listeners as $list) { + $listenersCount += count($list); + } + + self::assertEquals($expectedListeners, $listenersCount); } public function provideConfig(): iterable @@ -84,12 +107,12 @@ public function provideConfig(): iterable ], ]; - yield [array_merge($baseConfig, ['debug' => true]), 1, EntityRepository::class]; - yield [array_merge($baseConfig, ['debug' => '1']), 1, EntityRepository::class]; - yield [array_merge($baseConfig, ['debug' => 'true']), 1, EntityRepository::class]; - yield [array_merge($baseConfig, ['debug' => false]), 0, EntityRepository::class]; - yield [array_merge($baseConfig, ['debug' => null]), 0, EntityRepository::class]; - yield [array_merge($baseConfig, ['debug' => null]), 0, EntityRepository::class]; + yield [array_merge($baseConfig, ['debug' => true]), 1, EntityRepository::class, 0]; + yield [array_merge($baseConfig, ['debug' => '1']), 1, EntityRepository::class, 0]; + yield [array_merge($baseConfig, ['debug' => 'true']), 1, EntityRepository::class, 0]; + yield [array_merge($baseConfig, ['debug' => false]), 0, EntityRepository::class, 0]; + yield [array_merge($baseConfig, ['debug' => null]), 0, EntityRepository::class, 0]; + yield [array_merge($baseConfig, ['debug' => null]), 0, EntityRepository::class, 0]; yield [ array_merge_recursive($baseConfig, [ 'entity_manager' => [ @@ -98,6 +121,22 @@ public function provideConfig(): iterable ]), 0, CustomRepository::class, + 0, + ]; + yield [ + array_merge_recursive($baseConfig, [ + 'entity_manager' => [ + 'orm' => [ + 'listeners' => [ + Events::postFlush => ['foo_listener', 'bar_listener'], + Events::prePersist => ['foo_listener', 'bar_listener', 'baz_listener'], + ], + ], + ], + ]), + 0, + EntityRepository::class, + 5, ]; } }