Skip to content

Commit 9656263

Browse files
authored
Merge pull request #24 from patchlevel/aggragate-hashmap-in-store
add aggregate hashmap in store for shortname and tablename
2 parents b2f15fd + f7d7035 commit 9656263

5 files changed

Lines changed: 92 additions & 69 deletions

File tree

src/Store/AggregateNotDefined.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Patchlevel\EventSourcing\Store;
6+
7+
use function sprintf;
8+
9+
final class AggregateNotDefined extends StoreException
10+
{
11+
public function __construct(string $aggregate)
12+
{
13+
parent::__construct(sprintf('aggregate "%s" is not defined', $aggregate));
14+
}
15+
}

src/Store/MultiTableStore.php

Lines changed: 25 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,20 @@
1414
use Patchlevel\EventSourcing\Aggregate\AggregateChanged;
1515
use Patchlevel\EventSourcing\Aggregate\AggregateRoot;
1616

17+
use function array_key_exists;
18+
use function array_keys;
1719
use function array_map;
18-
use function array_pop;
19-
use function explode;
20-
use function preg_replace;
2120
use function sprintf;
22-
use function strtolower;
2321

2422
final class MultiTableStore implements Store
2523
{
2624
private Connection $connection;
2725

28-
/** @var list<class-string<AggregateRoot>> */
26+
/** @var array<class-string<AggregateRoot>, string> */
2927
private array $aggregates;
3028

3129
/**
32-
* @param list<class-string<AggregateRoot>> $aggregates
30+
* @param array<class-string<AggregateRoot>, string> $aggregates
3331
*/
3432
public function __construct(Connection $eventConnection, array $aggregates)
3533
{
@@ -44,7 +42,7 @@ public function __construct(Connection $eventConnection, array $aggregates)
4442
*/
4543
public function load(string $aggregate, string $id, int $fromPlayhead = -1): array
4644
{
47-
$tableName = self::tableName($aggregate);
45+
$tableName = $this->tableName($aggregate);
4846

4947
$sql = $this->connection->createQueryBuilder()
5048
->select('*')
@@ -78,7 +76,7 @@ static function (array $data) use ($platform) {
7876
*/
7977
public function has(string $aggregate, string $id): bool
8078
{
81-
$tableName = self::tableName($aggregate);
79+
$tableName = $this->tableName($aggregate);
8280

8381
$sql = $this->connection->createQueryBuilder()
8482
->select('COUNT(*)')
@@ -101,7 +99,7 @@ public function has(string $aggregate, string $id): bool
10199
*/
102100
public function saveBatch(string $aggregate, string $id, array $events): void
103101
{
104-
$tableName = self::tableName($aggregate);
102+
$tableName = $this->tableName($aggregate);
105103

106104
$this->connection->transactional(
107105
static function (Connection $connection) use ($tableName, $id, $events): void {
@@ -138,7 +136,7 @@ public function prepare(): void
138136

139137
public function drop(): void
140138
{
141-
foreach ($this->aggregates as $aggregate) {
139+
foreach (array_keys($this->aggregates) as $aggregate) {
142140
$this->dropTableForAggregate($aggregate);
143141
}
144142
}
@@ -148,7 +146,7 @@ public function drop(): void
148146
*/
149147
public function dropTableForAggregate(string $aggregate): void
150148
{
151-
$tableName = self::tableName($aggregate);
149+
$tableName = $this->tableName($aggregate);
152150

153151
$this->connection->executeQuery(sprintf('DROP TABLE IF EXISTS %s;', $tableName));
154152
}
@@ -157,19 +155,15 @@ private function schema(): Schema
157155
{
158156
$schema = new Schema([], [], $this->connection->getSchemaManager()->createSchemaConfig());
159157

160-
foreach ($this->aggregates as $aggregateClass) {
161-
$this->addTableToSchema($schema, $aggregateClass);
158+
foreach ($this->aggregates as $tableName) {
159+
$this->addTableToSchema($schema, $tableName);
162160
}
163161

164162
return $schema;
165163
}
166164

167-
/**
168-
* @param class-string<AggregateRoot> $aggregateClass
169-
*/
170-
private function addTableToSchema(Schema $schema, $aggregateClass): void
165+
private function addTableToSchema(Schema $schema, string $tableName): void
171166
{
172-
$tableName = self::tableName($aggregateClass);
173167
$table = $schema->createTable($tableName);
174168

175169
$table->addColumn('id', Types::BIGINT)
@@ -191,33 +185,12 @@ private function addTableToSchema(Schema $schema, $aggregateClass): void
191185
$table->addUniqueIndex(['aggregateId', 'playhead']);
192186
}
193187

194-
/**
195-
* @param class-string<AggregateRoot> $name
196-
*/
197-
private static function tableName(string $name): string
198-
{
199-
$parts = explode('\\', $name);
200-
$shortName = array_pop($parts);
201-
202-
if (!$shortName) {
203-
throw new StoreException(sprintf('%s is not a valid classname', $name));
204-
}
205-
206-
$string = (string)preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $shortName);
207-
208-
if (!$string) {
209-
throw new StoreException(sprintf('%s is not a valid table name', $string));
210-
}
211-
212-
return strtolower($string);
213-
}
214-
215188
/**
216189
* @param array<string, mixed> $result
217190
*
218191
* @return array<string, mixed>
219192
*/
220-
public static function normalizeResult(AbstractPlatform $platform, array $result): array
193+
private static function normalizeResult(AbstractPlatform $platform, array $result): array
221194
{
222195
if (!$result['recordedOn']) {
223196
return $result;
@@ -236,4 +209,16 @@ public static function normalizeResult(AbstractPlatform $platform, array $result
236209

237210
return $result;
238211
}
212+
213+
/**
214+
* @param class-string<AggregateRoot> $aggregate
215+
*/
216+
private function tableName(string $aggregate): string
217+
{
218+
if (!array_key_exists($aggregate, $this->aggregates)) {
219+
throw new AggregateNotDefined($aggregate);
220+
}
221+
222+
return $this->aggregates[$aggregate];
223+
}
239224
}

src/Store/SingleTableStore.php

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@
1414
use Generator;
1515
use Patchlevel\EventSourcing\Aggregate\AggregateChanged;
1616
use Patchlevel\EventSourcing\Aggregate\AggregateRoot;
17-
use RuntimeException;
1817

18+
use function array_key_exists;
1919
use function array_map;
20-
use function array_pop;
21-
use function explode;
2220
use function sprintf;
2321

2422
final class SingleTableStore implements Store
@@ -27,9 +25,16 @@ final class SingleTableStore implements Store
2725

2826
private Connection $connection;
2927

30-
public function __construct(Connection $connection)
28+
/** @var array<class-string<AggregateRoot>, string> */
29+
private array $aggregates;
30+
31+
/**
32+
* @param array<class-string<AggregateRoot>, string> $aggregates
33+
*/
34+
public function __construct(Connection $connection, array $aggregates)
3135
{
3236
$this->connection = $connection;
37+
$this->aggregates = $aggregates;
3338
}
3439

3540
/**
@@ -39,6 +44,8 @@ public function __construct(Connection $connection)
3944
*/
4045
public function load(string $aggregate, string $id, int $fromPlayhead = -1): array
4146
{
47+
$shortName = $this->shortName($aggregate);
48+
4249
$sql = $this->connection->createQueryBuilder()
4350
->select('*')
4451
->from(self::TABLE_NAME)
@@ -48,7 +55,7 @@ public function load(string $aggregate, string $id, int $fromPlayhead = -1): arr
4855
$result = $this->connection->fetchAllAssociative(
4956
$sql,
5057
[
51-
'aggregate' => self::shortName($aggregate),
58+
'aggregate' => $shortName,
5259
'id' => $id,
5360
'playhead' => $fromPlayhead,
5461
]
@@ -93,6 +100,8 @@ public function loadAll(): Generator
93100
*/
94101
public function has(string $aggregate, string $id): bool
95102
{
103+
$shortName = $this->shortName($aggregate);
104+
96105
$sql = $this->connection->createQueryBuilder()
97106
->select('COUNT(*)')
98107
->from(self::TABLE_NAME)
@@ -103,7 +112,7 @@ public function has(string $aggregate, string $id): bool
103112
$result = (int)$this->connection->fetchOne(
104113
$sql,
105114
[
106-
'aggregate' => self::shortName($aggregate),
115+
'aggregate' => $shortName,
107116
'id' => $id,
108117
]
109118
);
@@ -127,15 +136,17 @@ public function count(): int
127136
*/
128137
public function saveBatch(string $aggregate, string $id, array $events): void
129138
{
139+
$shortName = $this->shortName($aggregate);
140+
130141
$this->connection->transactional(
131-
static function (Connection $connection) use ($aggregate, $id, $events): void {
142+
static function (Connection $connection) use ($shortName, $id, $events): void {
132143
foreach ($events as $event) {
133144
if ($event->aggregateId() !== $id) {
134145
throw new StoreException('id missmatch');
135146
}
136147

137148
$data = $event->serialize();
138-
$data['aggregate'] = self::shortName($aggregate);
149+
$data['aggregate'] = $shortName;
139150

140151
$connection->insert(
141152
self::TABLE_NAME,
@@ -199,27 +210,12 @@ private function addTableToSchema(Schema $schema): void
199210
$table->addUniqueIndex(['aggregate', 'aggregateId', 'playhead']);
200211
}
201212

202-
/**
203-
* @param class-string<AggregateRoot> $name
204-
*/
205-
private static function shortName(string $name): string
206-
{
207-
$parts = explode('\\', $name);
208-
$shortName = array_pop($parts);
209-
210-
if (!$shortName) {
211-
throw new RuntimeException(sprintf('%s is not a valid classname', $name));
212-
}
213-
214-
return $shortName;
215-
}
216-
217213
/**
218214
* @param array<string, mixed> $result
219215
*
220216
* @return array<string, mixed>
221217
*/
222-
public static function normalizeResult(AbstractPlatform $platform, array $result): array
218+
private static function normalizeResult(AbstractPlatform $platform, array $result): array
223219
{
224220
if (!$result['recordedOn']) {
225221
return $result;
@@ -238,4 +234,16 @@ public static function normalizeResult(AbstractPlatform $platform, array $result
238234

239235
return $result;
240236
}
237+
238+
/**
239+
* @param class-string<AggregateRoot> $aggregate
240+
*/
241+
private function shortName(string $aggregate): string
242+
{
243+
if (!array_key_exists($aggregate, $this->aggregates)) {
244+
throw new AggregateNotDefined($aggregate);
245+
}
246+
247+
return $this->aggregates[$aggregate];
248+
}
241249
}

src/Store/StoreException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66

77
use RuntimeException;
88

9-
final class StoreException extends RuntimeException
9+
class StoreException extends RuntimeException
1010
{
1111
}

tests/Integration/BasicImplementation/BasicIntegrationTest.php

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ public function testSuccessful(): void
5959
$eventStream->addListener(new ProjectionListener($projectionRepository));
6060
$eventStream->addListener(new SendEmailProcessor());
6161

62-
$store = new SingleTableStore($this->connection);
62+
$store = new SingleTableStore(
63+
$this->connection,
64+
[Profile::class => 'profile']
65+
);
66+
6367
$repository = new Repository($store, $eventStream, Profile::class);
6468

6569
// create tables
@@ -92,7 +96,11 @@ public function testWithSymfonySuccessful(): void
9296
new SendEmailProcessor(),
9397
]);
9498

95-
$store = new SingleTableStore($this->connection);
99+
$store = new SingleTableStore(
100+
$this->connection,
101+
[Profile::class => 'profile']
102+
);
103+
96104
$repository = new Repository($store, $eventStream, Profile::class);
97105

98106
// create tables
@@ -124,7 +132,10 @@ public function testMultiTableSuccessful(): void
124132
$eventStream->addListener(new ProjectionListener($projectionRepository));
125133
$eventStream->addListener(new SendEmailProcessor());
126134

127-
$store = new MultiTableStore($this->connection, [Profile::class]);
135+
$store = new MultiTableStore(
136+
$this->connection,
137+
[Profile::class => 'profile']
138+
);
128139

129140
$repository = new Repository($store, $eventStream, Profile::class);
130141

@@ -157,7 +168,11 @@ public function testSnapshot(): void
157168
$eventStream->addListener(new ProjectionListener($projectionRepository));
158169
$eventStream->addListener(new SendEmailProcessor());
159170

160-
$store = new SingleTableStore($this->connection);
171+
$store = new SingleTableStore(
172+
$this->connection,
173+
[Profile::class => 'profile']
174+
);
175+
161176
$snapshotStore = new InMemorySnapshotStore();
162177

163178
$repository = new Repository($store, $eventStream, Profile::class, $snapshotStore);

0 commit comments

Comments
 (0)