diff --git a/.docs/README.md b/.docs/README.md index 901c426..2fed693 100755 --- a/.docs/README.md +++ b/.docs/README.md @@ -9,6 +9,7 @@ Integration of [Doctrine ORM](https://www.doctrine-project.org/projects/orm.html - [Minimal configuration](#minimal-configuration) - [Advanced configuration](#advanced-configuration) - [Auto configuration](#auto-configuration) + - [EntityManager](#entitymanager) - [Caching](#caching) - [Mapping](#mapping) - [Attributes](#attributes) @@ -152,6 +153,37 @@ By default, this extension will try to autoconfigure itself. - `3` means that the proxy classes are generated automatically using `eval()` (useful for debugging). - `4` means that the proxy classes are generated automatically when the proxy file does not exist or when the proxied file changed. +### EntityManager + +EntityManager is a central access point to ORM functionality. It is a wrapper around ObjectManager and holds the metadata and configuration of the ORM. + +**EntityManagerDecorator** + +You can use `entityManagerDecoratorClass` to decorate EntityManager. + +```neon +nettrine.orm: + managers: + default: + connection: default + entityManagerDecoratorClass: App\MyEntityManagerDecorator +``` + +**Close & Reset** + +If you hit `The EntityManager is closed.` exception, you can use `reset` method to reopen it. + +```php +$managerRegistry = $container->getByType(Doctrine\Persistence\ManagerRegistry::class); +$managerRegistry->resetManager(); // default +$managerRegistry->resetManager('second'); +``` + +> [!WARNING] +> Resetting the manager is a dangerous operation. It is also black magic, because you cannot just create a new EntityManager instance, +> you have to reset the current one using internal methods (reflection & binding). +> Class responsible for this operation is [`Nettrine\ORM\ManagerRegistry`](https://github.com/contributte/doctrine-orm/blob/master/src/ManagerRegistry.php). + ### Caching > [!TIP] @@ -302,6 +334,7 @@ The XML mapping driver enables you to provide the ORM metadata in form of XML do > - https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/xml-mapping.html ```xml + container->getService($name); + + Binder::use($manager, function (): void { + /** @var EntityManager $this */ + $this->closed = false; // @phpstan-ignore-line + }); + $this->container->removeService($name); } diff --git a/src/Utils/Binder.php b/src/Utils/Binder.php new file mode 100644 index 0000000..c8bbbc0 --- /dev/null +++ b/src/Utils/Binder.php @@ -0,0 +1,18 @@ +bindTo(is_object($objectOrClass) ? $objectOrClass : null, $objectOrClass)(); + } + +} diff --git a/tests/Cases/ManagerRegistry.phpt b/tests/Cases/ManagerRegistry.phpt index aae9c35..ede22fd 100644 --- a/tests/Cases/ManagerRegistry.phpt +++ b/tests/Cases/ManagerRegistry.phpt @@ -3,6 +3,7 @@ use Contributte\Tester\Toolkit; use Contributte\Tester\Utils\ContainerBuilder; use Contributte\Tester\Utils\Neonkit; +use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ManagerRegistry; use Nette\DI\Compiler; use Nettrine\DBAL\DI\DbalExtension; @@ -12,6 +13,7 @@ use Tests\Toolkit\Tests; require_once __DIR__ . '/../bootstrap.php'; +// Multiple managers Toolkit::test(function (): void { $container = ContainerBuilder::of() ->withCompiler(function (Compiler $compiler): void { @@ -63,3 +65,73 @@ Toolkit::test(function (): void { Assert::count(2, $registry->getManagers()); }); + +// Reset manager +Toolkit::test(function (): void { + $container = ContainerBuilder::of() + ->withCompiler(function (Compiler $compiler): void { + $compiler->addExtension('nettrine.dbal', new DbalExtension()); + $compiler->addExtension('nettrine.orm', new OrmExtension()); + $compiler->addConfig([ + 'parameters' => [ + 'tempDir' => Tests::TEMP_PATH, + ], + ]); + $compiler->addConfig(Neonkit::load( + <<<'NEON' + nettrine.dbal: + connections: + default: + driver: pdo_sqlite + password: test + user: test + path: ":memory:" + second: + driver: pdo_sqlite + password: test + user: test + path: ":memory:" + nettrine.orm: + managers: + default: + connection: default + mapping: + App: + type: attributes + dirs: [app/Database] + namespace: App\Database + second: + connection: second + entityManagerDecoratorClass: Tests\Mocks\DummyEntityManagerDecorator + mapping: + App: + type: attributes + dirs: [app/Database] + namespace: App\Database + NEON + )); + }) + ->build(); + + /** @var ManagerRegistry $registry */ + $registry = $container->getByType(ManagerRegistry::class); + + foreach (['default', 'second'] as $managerName) { + /** @var EntityManagerInterface $em1 */ + $em1 = $registry->getManager($managerName); + + Assert::true($em1->isOpen()); + $em1->close(); + Assert::false($em1->isOpen()); + + // Reset manager + $registry->resetManager($managerName); + + /** @var EntityManagerInterface $em2 */ + $em2 = $registry->getManager(); + Assert::notSame($em1, $em2); + + Assert::true($em1->isOpen()); + Assert::true($em2->isOpen()); + } +});