Skip to content

Commit 2941bca

Browse files
committed
ManagerRegistry: support reseting entity manager [#104]
1 parent ae5e762 commit 2941bca

File tree

4 files changed

+132
-0
lines changed

4 files changed

+132
-0
lines changed

.docs/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Integration of [Doctrine ORM](https://www.doctrine-project.org/projects/orm.html
99
- [Minimal configuration](#minimal-configuration)
1010
- [Advanced configuration](#advanced-configuration)
1111
- [Auto configuration](#auto-configuration)
12+
- [EntityManager](#entitymanager)
1213
- [Caching](#caching)
1314
- [Mapping](#mapping)
1415
- [Attributes](#attributes)
@@ -152,6 +153,37 @@ By default, this extension will try to autoconfigure itself.
152153
- `3` means that the proxy classes are generated automatically using `eval()` (useful for debugging).
153154
- `4` means that the proxy classes are generated automatically when the proxy file does not exist or when the proxied file changed.
154155

156+
### EntityManager
157+
158+
EntityManager is a central access point to ORM functionality. It is a wrapper around ObjectManager and holds the metadata and configuration of the ORM.
159+
160+
**EntityManagerDecorator**
161+
162+
You can use `entityManagerDecoratorClass` to decorate EntityManager.
163+
164+
```neon
165+
nettrine.orm:
166+
managers:
167+
default:
168+
connection: default
169+
entityManagerDecoratorClass: App\MyEntityManagerDecorator
170+
```
171+
172+
**Close & Reset**
173+
174+
If you hit `The EntityManager is closed.` exception, you can use `reset` method to reopen it.
175+
176+
```php
177+
$managerRegistry = $container->getByType(Doctrine\Persistence\ManagerRegistry::class);
178+
$managerRegistry->resetManager(); // default
179+
$managerRegistry->resetManager('second');
180+
```
181+
182+
> [!WARNING]
183+
> Resetting the manager is a dangerous operation. It is also black magic, because you cannot just create a new EntityManager instance,
184+
> you have to reset the current one using internal methods (reflection & binding).
185+
> Class responsible for this operation is [`Nettrine\ORM\ManagerRegistry`](https://github.com/contributte/doctrine-orm/blob/master/src/ManagerRegistry.php).
186+
155187
### Caching
156188

157189
> [!TIP]
@@ -302,6 +334,7 @@ The XML mapping driver enables you to provide the ORM metadata in form of XML do
302334
> - https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/xml-mapping.html
303335
304336
```xml
337+
305338
<doctrine-mapping
306339
xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
307340
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"

src/ManagerRegistry.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
namespace Nettrine\ORM;
44

5+
use Doctrine\ORM\EntityManager;
56
use Doctrine\Persistence\AbstractManagerRegistry;
67
use Doctrine\Persistence\Proxy;
78
use Nette\DI\Container;
9+
use Nettrine\ORM\Utils\Binder;
810

911
class ManagerRegistry extends AbstractManagerRegistry
1012
{
@@ -40,6 +42,13 @@ protected function getService(string $name): object
4042

4143
protected function resetService(string $name): void
4244
{
45+
$manager = $this->container->getService($name);
46+
47+
Binder::use($manager, function (): void {
48+
/** @var EntityManager $this */
49+
$this->closed = false; // @phpstan-ignore-line
50+
});
51+
4352
$this->container->removeService($name);
4453
}
4554

src/Utils/Binder.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Nettrine\ORM\Utils;
4+
5+
use Closure;
6+
7+
final class Binder
8+
{
9+
10+
/**
11+
* @param object|class-string $objectOrClass
12+
*/
13+
public static function use(object|string $objectOrClass, Closure $closure): mixed
14+
{
15+
return $closure->bindTo(is_object($objectOrClass) ? $objectOrClass : null, $objectOrClass)();
16+
}
17+
18+
}

tests/Cases/ManagerRegistry.phpt

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use Contributte\Tester\Toolkit;
44
use Contributte\Tester\Utils\ContainerBuilder;
55
use Contributte\Tester\Utils\Neonkit;
6+
use Doctrine\ORM\EntityManagerInterface;
67
use Doctrine\Persistence\ManagerRegistry;
78
use Nette\DI\Compiler;
89
use Nettrine\DBAL\DI\DbalExtension;
@@ -12,6 +13,7 @@ use Tests\Toolkit\Tests;
1213

1314
require_once __DIR__ . '/../bootstrap.php';
1415

16+
// Multiple managers
1517
Toolkit::test(function (): void {
1618
$container = ContainerBuilder::of()
1719
->withCompiler(function (Compiler $compiler): void {
@@ -63,3 +65,73 @@ Toolkit::test(function (): void {
6365

6466
Assert::count(2, $registry->getManagers());
6567
});
68+
69+
// Reset manager
70+
Toolkit::test(function (): void {
71+
$container = ContainerBuilder::of()
72+
->withCompiler(function (Compiler $compiler): void {
73+
$compiler->addExtension('nettrine.dbal', new DbalExtension());
74+
$compiler->addExtension('nettrine.orm', new OrmExtension());
75+
$compiler->addConfig([
76+
'parameters' => [
77+
'tempDir' => Tests::TEMP_PATH,
78+
],
79+
]);
80+
$compiler->addConfig(Neonkit::load(
81+
<<<'NEON'
82+
nettrine.dbal:
83+
connections:
84+
default:
85+
driver: pdo_sqlite
86+
password: test
87+
user: test
88+
path: ":memory:"
89+
second:
90+
driver: pdo_sqlite
91+
password: test
92+
user: test
93+
path: ":memory:"
94+
nettrine.orm:
95+
managers:
96+
default:
97+
connection: default
98+
mapping:
99+
App:
100+
type: attributes
101+
dirs: [app/Database]
102+
namespace: App\Database
103+
second:
104+
connection: second
105+
entityManagerDecoratorClass: Tests\Mocks\DummyEntityManagerDecorator
106+
mapping:
107+
App:
108+
type: attributes
109+
dirs: [app/Database]
110+
namespace: App\Database
111+
NEON
112+
));
113+
})
114+
->build();
115+
116+
/** @var ManagerRegistry $registry */
117+
$registry = $container->getByType(ManagerRegistry::class);
118+
119+
foreach (['default', 'second'] as $managerName) {
120+
/** @var EntityManagerInterface $em1 */
121+
$em1 = $registry->getManager($managerName);
122+
123+
Assert::true($em1->isOpen());
124+
$em1->close();
125+
Assert::false($em1->isOpen());
126+
127+
// Reset manager
128+
$registry->resetManager($managerName);
129+
130+
/** @var EntityManagerInterface $em2 */
131+
$em2 = $registry->getManager();
132+
Assert::notSame($em1, $em2);
133+
134+
Assert::true($em1->isOpen());
135+
Assert::true($em2->isOpen());
136+
}
137+
});

0 commit comments

Comments
 (0)