Skip to content

Commit 8f4ecdb

Browse files
authored
Merge pull request #820 from patchlevel/conection-registry-dbal-cleanup-task-handler
allow to pass connection registry in dbal cleanup task handler
2 parents 4c37ff0 + b05a448 commit 8f4ecdb

8 files changed

Lines changed: 160 additions & 6 deletions

File tree

docs/pages/aggregate.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ final class Profile extends BasicAggregateRoot
365365

366366
## Shared apply context
367367

368-
When working with [micro-aggregates](./aggregate.md#micro-aggregates),
368+
When working with [micro-aggregates](./aggregate.md#micro-aggregates),
369369
it’s common that events are applied by different aggregates.
370370
As a result, an aggregate may receive events it does not handle, which can lead to multiple “missing apply” warnings.
371371

docs/pages/subscription.md

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,11 @@ Default, we provide the following cleanup tasks for `doctrine/dbal`:
422422
| `DropIndexTask` | Drops an index from a table. |
423423
| `DropTableTask` | Drops a table. |
424424

425+
!!! note
426+
427+
If you are passing connection registry, you can use the connection name as parameter.
428+
The `connectionName` parameter is optional and defaults to the default connection.
429+
425430
!!! tip
426431

427432
You can create your own cleanup tasks and handler.
@@ -1054,18 +1059,45 @@ Lastly, we have to add the new handler to `DefaultCleaner`,
10541059
which is responsible for cleaning up subscriptions.
10551060

10561061
```php
1057-
use Patchlevel\EventSourcing\Subscription\Cleanup\Dbal\DbalCleanupTaskHandler;
10581062
use Patchlevel\EventSourcing\Subscription\Cleanup\DefaultCleaner;
10591063

10601064
$cleaner = new DefaultCleaner([
1061-
new DbalCleanupTaskHandler($projectionConnection),
10621065
new MongodbCleanupTaskHandler($mongodbDatabase),
10631066
]);
10641067
```
10651068
!!! warning
10661069

10671070
You need to pass the Cleaner to the Subscription Engine.
10681071

1072+
#### Dbal Cleanup Task Handler
1073+
1074+
We provide a Dbal cleanup task handler by default.
1075+
More information about the available tasks can be found in the [Dbal Cleanup Tasks](#dbal-cleanup-tasks) documentation.
1076+
1077+
```php
1078+
use Doctrine\Dbal\Connection;
1079+
use Patchlevel\EventSourcing\Subscription\Cleanup\Dbal\DbalCleanupTaskHandler;
1080+
use Patchlevel\EventSourcing\Subscription\Cleanup\DefaultCleaner;
1081+
1082+
/** @var Connection $connection */
1083+
$cleaner = new DefaultCleaner([
1084+
new DbalCleanupTaskHandler($connection),
1085+
]);
1086+
```
1087+
If you have multiple database connections and want to use the `DbalCleanupTaskHandler` to clean up the respective databases,
1088+
you can also pass a `ConnectionRegistry` (from `doctrine/persistence`) to the `DbalCleanupTaskHandler`.
1089+
Then you can pass the connection name as parameter in the cleanup task and the handler will use the corresponding connection to execute the task.
1090+
1091+
```php
1092+
use Doctrine\Persistence\ConnectionRegistry;
1093+
use Patchlevel\EventSourcing\Subscription\Cleanup\Dbal\DbalCleanupTaskHandler;
1094+
use Patchlevel\EventSourcing\Subscription\Cleanup\DefaultCleaner;
1095+
1096+
/** @var ConnectionRegistry $connectionRegistry */
1097+
$cleaner = new DefaultCleaner([
1098+
new DbalCleanupTaskHandler($connectionRegistry),
1099+
]);
1100+
```
10691101
### Subscriber Accessor
10701102

10711103
The subscriber accessor repository is responsible for providing the subscribers to the subscription engine.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcing\Subscription\Cleanup\Dbal;
6+
7+
use RuntimeException;
8+
9+
use function sprintf;
10+
11+
final class ConnectionNameNotSupported extends RuntimeException
12+
{
13+
public function __construct(string $connectionName)
14+
{
15+
parent::__construct(
16+
sprintf(
17+
'Connection name "%s" is not supported. Only a single connection is available. Please use the connection registry.',
18+
$connectionName,
19+
),
20+
);
21+
}
22+
}

src/Subscription/Cleanup/Dbal/DbalCleanupTaskHandler.php

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,27 @@
55
namespace Patchlevel\EventSourcing\Subscription\Cleanup\Dbal;
66

77
use Doctrine\DBAL\Connection;
8+
use Doctrine\Persistence\ConnectionRegistry;
89
use Patchlevel\EventSourcing\Subscription\Cleanup\CleanupTaskHandler;
910
use Patchlevel\EventSourcing\Subscription\Cleanup\CleanupTaskNotSupported;
1011

1112
final class DbalCleanupTaskHandler implements CleanupTaskHandler
1213
{
1314
public function __construct(
14-
private readonly Connection $connection,
15+
private readonly Connection|ConnectionRegistry $connection,
1516
) {
1617
}
1718

1819
public function __invoke(object $task): void
1920
{
2021
if ($task instanceof DropTableTask) {
21-
$this->connection->createSchemaManager()->dropTable($task->table);
22+
$this->connection($task->connectionName)->createSchemaManager()->dropTable($task->table);
2223

2324
return;
2425
}
2526

2627
if ($task instanceof DropIndexTask) {
27-
$this->connection->createSchemaManager()->dropIndex($task->index, $task->table);
28+
$this->connection($task->connectionName)->createSchemaManager()->dropIndex($task->index, $task->table);
2829

2930
return;
3031
}
@@ -36,4 +37,23 @@ public function supports(object $task): bool
3637
{
3738
return $task instanceof DropTableTask || $task instanceof DropIndexTask;
3839
}
40+
41+
private function connection(string|null $connectionName): Connection
42+
{
43+
if ($this->connection instanceof ConnectionRegistry) {
44+
$connection = $this->connection->getConnection($connectionName);
45+
46+
if (!$connection instanceof Connection) {
47+
throw new UnexpectedConnectionType($connectionName, Connection::class, $connection::class);
48+
}
49+
50+
return $connection;
51+
}
52+
53+
if ($connectionName === null) {
54+
return $this->connection;
55+
}
56+
57+
throw new ConnectionNameNotSupported($connectionName);
58+
}
3959
}

src/Subscription/Cleanup/Dbal/DropIndexTask.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ final class DropIndexTask
99
public function __construct(
1010
public readonly string $index,
1111
public readonly string $table,
12+
public readonly string|null $connectionName = null,
1213
) {
1314
}
1415
}

src/Subscription/Cleanup/Dbal/DropTableTask.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ final class DropTableTask
88
{
99
public function __construct(
1010
public readonly string $table,
11+
public readonly string|null $connectionName = null,
1112
) {
1213
}
1314
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcing\Subscription\Cleanup\Dbal;
6+
7+
use RuntimeException;
8+
9+
use function sprintf;
10+
11+
final class UnexpectedConnectionType extends RuntimeException
12+
{
13+
/**
14+
* @param class-string $expected
15+
* @param class-string $actual
16+
*/
17+
public function __construct(string|null $connectionName, string $expected, string $actual)
18+
{
19+
parent::__construct(
20+
sprintf(
21+
'Expected connection "%s" to be of type "%s", got "%s"',
22+
$connectionName ?? 'default (null)',
23+
$expected,
24+
$actual,
25+
),
26+
);
27+
}
28+
}

tests/Unit/Subscription/Cleanup/Dbal/DbalCleanupTaskHandlerTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66

77
use Doctrine\DBAL\Connection;
88
use Doctrine\DBAL\Schema\AbstractSchemaManager;
9+
use Doctrine\Persistence\ConnectionRegistry;
910
use Patchlevel\EventSourcing\Subscription\Cleanup\CleanupTaskNotSupported;
11+
use Patchlevel\EventSourcing\Subscription\Cleanup\Dbal\ConnectionNameNotSupported;
1012
use Patchlevel\EventSourcing\Subscription\Cleanup\Dbal\DbalCleanupTaskHandler;
1113
use Patchlevel\EventSourcing\Subscription\Cleanup\Dbal\DropIndexTask;
1214
use Patchlevel\EventSourcing\Subscription\Cleanup\Dbal\DropTableTask;
15+
use Patchlevel\EventSourcing\Subscription\Cleanup\Dbal\UnexpectedConnectionType;
1316
use PHPUnit\Framework\Attributes\CoversClass;
1417
use PHPUnit\Framework\TestCase;
1518
use stdClass;
@@ -78,4 +81,51 @@ public function testHandleDropIndex(): void
7881

7982
$handler(new DropIndexTask('foo', 'bar'));
8083
}
84+
85+
public function testHandleWithConnectionRegistry(): void
86+
{
87+
$schemaManager = $this->createMock(AbstractSchemaManager::class);
88+
$schemaManager->expects($this->once())->method('dropTable')->with('test');
89+
90+
$connection = $this->createMock(Connection::class);
91+
$connection
92+
->expects($this->once())
93+
->method('createSchemaManager')
94+
->willReturn($schemaManager);
95+
96+
$registry = $this->createMock(ConnectionRegistry::class);
97+
$registry
98+
->expects($this->once())
99+
->method('getConnection')
100+
->with('foo')
101+
->willReturn($connection);
102+
103+
$handler = new DbalCleanupTaskHandler($registry);
104+
105+
$handler(new DropTableTask('test', 'foo'));
106+
}
107+
108+
public function testHandleWithConnectionRegistryAndUnexpectedType(): void
109+
{
110+
$registry = $this->createMock(ConnectionRegistry::class);
111+
$registry
112+
->expects($this->once())
113+
->method('getConnection')
114+
->with('foo')
115+
->willReturn(new stdClass());
116+
117+
$handler = new DbalCleanupTaskHandler($registry);
118+
119+
$this->expectException(UnexpectedConnectionType::class);
120+
$handler(new DropTableTask('test', 'foo'));
121+
}
122+
123+
public function testHandleWithConnectionAndConnectionNameNotSupported(): void
124+
{
125+
$connection = $this->createMock(Connection::class);
126+
$handler = new DbalCleanupTaskHandler($connection);
127+
128+
$this->expectException(ConnectionNameNotSupported::class);
129+
$handler(new DropTableTask('test', 'foo'));
130+
}
81131
}

0 commit comments

Comments
 (0)