Skip to content

Commit 6c88df2

Browse files
committed
add subscription cleanup feature
1 parent 68bc84e commit 6c88df2

16 files changed

Lines changed: 496 additions & 1 deletion

src/Attribute/Cleanup.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcing\Attribute;
6+
7+
use Attribute;
8+
9+
#[Attribute(Attribute::TARGET_METHOD)]
10+
final class Cleanup
11+
{
12+
}

src/Metadata/Subscriber/AttributeSubscriberMetadataFactory.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace Patchlevel\EventSourcing\Metadata\Subscriber;
66

7+
use Patchlevel\EventSourcing\Attribute\Cleanup;
78
use Patchlevel\EventSourcing\Attribute\OnFailed;
89
use Patchlevel\EventSourcing\Attribute\RetryStrategy;
910
use Patchlevel\EventSourcing\Attribute\Setup;
@@ -45,6 +46,7 @@ public function metadata(string $subscriber): SubscriberMetadata
4546
$subscribeMethods = [];
4647
$setupMethod = null;
4748
$teardownMethod = null;
49+
$cleanupMethod = null;
4850
$failedMethod = null;
4951

5052
foreach ($methods as $method) {
@@ -90,6 +92,18 @@ public function metadata(string $subscriber): SubscriberMetadata
9092
$setupMethod = $method->getName();
9193
}
9294

95+
if ($method->getAttributes(Cleanup::class)) {
96+
if ($cleanupMethod !== null) {
97+
throw new DuplicateCleanupMethod(
98+
$subscriber,
99+
$cleanupMethod,
100+
$method->getName(),
101+
);
102+
}
103+
104+
$cleanupMethod = $method->getName();
105+
}
106+
93107
if (!$method->getAttributes(Teardown::class)) {
94108
continue;
95109
}
@@ -118,6 +132,7 @@ public function metadata(string $subscriber): SubscriberMetadata
118132
$teardownMethod,
119133
$failedMethod,
120134
$this->retryStrategy($reflector),
135+
$cleanupMethod,
121136
);
122137

123138
$this->subscriberMetadata[$subscriber] = $metadata;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcing\Metadata\Subscriber;
6+
7+
use Patchlevel\EventSourcing\Metadata\MetadataException;
8+
9+
use function sprintf;
10+
11+
final class DuplicateCleanupMethod extends MetadataException
12+
{
13+
/** @param class-string $subscriber */
14+
public function __construct(string $subscriber, string $fistMethod, string $secondMethod)
15+
{
16+
parent::__construct(
17+
sprintf(
18+
'Two methods "%s" and "%s" on the subscriber "%s" have been marked as "cleanup" methods. Only one method can be defined like this.',
19+
$fistMethod,
20+
$secondMethod,
21+
$subscriber,
22+
),
23+
);
24+
}
25+
}

src/Metadata/Subscriber/SubscriberMetadata.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public function __construct(
1919
public readonly string|null $teardownMethod = null,
2020
public readonly string|null $failedMethod = null,
2121
public readonly string|null $retryStrategy = null,
22+
public readonly string|null $cleanupMethod = null,
2223
) {
2324
}
2425
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcing\Subscription\Cleanup;
6+
7+
use Patchlevel\EventSourcing\Subscription\Subscription;
8+
9+
interface Cleaner
10+
{
11+
public function cleanup(Subscription $subscription): void;
12+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcing\Subscription\Cleanup;
6+
7+
interface CleanupHandler
8+
{
9+
public function __invoke(object $task): void;
10+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcing\Subscription\Cleanup\Dbal;
6+
7+
use Doctrine\DBAL\Connection;
8+
use Patchlevel\EventSourcing\Subscription\Cleanup\CleanupHandler;
9+
10+
final class DbalCleanupHandler implements CleanupHandler
11+
{
12+
public function __construct(
13+
private readonly Connection $connection,
14+
) {
15+
}
16+
17+
public function __invoke(object $task): void
18+
{
19+
if ($task instanceof RemoveDbalTableTask) {
20+
$this->connection->createSchemaManager()->dropTable($task->table);
21+
}
22+
23+
if ($task instanceof RemoveDbalIndexTask) {
24+
$this->connection->createSchemaManager()->dropIndex($task->index, $task->table);
25+
}
26+
}
27+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcing\Subscription\Cleanup\Dbal;
6+
7+
final class RemoveDbalIndexTask
8+
{
9+
public function __construct(
10+
public readonly string $index,
11+
public readonly string $table
12+
) {
13+
}
14+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcing\Subscription\Cleanup\Dbal;
6+
7+
final class RemoveDbalTableTask
8+
{
9+
public function __construct(public readonly string $table)
10+
{
11+
}
12+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcing\Subscription\Cleanup;
6+
7+
use Patchlevel\EventSourcing\Subscription\Subscription;
8+
use Psr\Log\LoggerInterface;
9+
10+
class DefaultCleaner implements Cleaner
11+
{
12+
/** @param iterable<CleanupHandler> $handlers */
13+
public function __construct(
14+
private readonly iterable $handlers,
15+
private readonly LoggerInterface|null $logger = null,
16+
) {
17+
}
18+
19+
public function cleanup(Subscription $subscription): void
20+
{
21+
$tasks = $subscription->cleanupTasks();
22+
23+
if (!$tasks) {
24+
$this->logger?->debug('no cleanup tasks found');
25+
26+
return;
27+
}
28+
29+
foreach ($tasks as $task) {
30+
foreach ($this->handlers as $handler) {
31+
$handler($task);
32+
}
33+
}
34+
}
35+
}

0 commit comments

Comments
 (0)