From 1dd0e3f7bc7ecb17cfd395d5fffe4487d9e7f47c Mon Sep 17 00:00:00 2001 From: prolic Date: Wed, 25 Jan 2017 19:20:38 +0800 Subject: [PATCH] change snapshot store interfaces resolves: https://github.com/prooph/pdo-snapshot-store/issues/5 --- src/PdoSnapshotStore.php | 64 +++++++++++++++++++++++----------- tests/PdoSnapshotStoreTest.php | 43 +++++++++++++++++++++++ 2 files changed, 87 insertions(+), 20 deletions(-) diff --git a/src/PdoSnapshotStore.php b/src/PdoSnapshotStore.php index ce08693..d7da053 100644 --- a/src/PdoSnapshotStore.php +++ b/src/PdoSnapshotStore.php @@ -71,34 +71,58 @@ public function get(string $aggregateType, string $aggregateId): ?Snapshot ); } - public function save(Snapshot $snapshot): void + public function save(Snapshot ...$snapshots): void { - $table = $this->getTableName($snapshot->aggregateType()); + if (empty($snapshots)) { + return; + } - $this->connection->beginTransaction(); + $deletes = []; + $inserts = []; + + foreach ($snapshots as $snapshot) { + $deletes[$this->getTableName($snapshot->aggregateType())][] = $snapshot->aggregateId(); + $inserts[$this->getTableName($snapshot->aggregateType())][] = $snapshot; + } + + $statements = []; - $delete = << $aggregateIds) { + $ids = implode(', ', array_fill(0, count($aggregateIds), '?')); + $deleteSql = <<connection->prepare($deleteSql); + foreach ($aggregateIds as $position => $aggregateId) { + $statement->bindValue($position + 1, $aggregateId); + } - $statement = $this->connection->prepare($delete); - $statement->execute([ - $snapshot->aggregateId(), - ]); + $statements[] = $statement; + } - $insert = << $snapshots) { + $allPlaces = implode(', ', array_fill(0, count($snapshots), '(?, ?, ?, ?, ?)')); + $insertSql = <<connection->prepare($insertSql); + foreach ($snapshots as $index => $snapshot) { + $position = $index * 5; + $statement->bindValue(++$position, $snapshot->aggregateId()); + $statement->bindValue(++$position, $snapshot->aggregateType()); + $statement->bindValue(++$position, $snapshot->lastVersion(), PDO::PARAM_INT); + $statement->bindValue(++$position, $snapshot->createdAt()->format('Y-m-d\TH:i:s.u')); + $statement->bindValue(++$position, serialize($snapshot->aggregateRoot())); + } + $statements[] = $statement; + } - $statement = $this->connection->prepare($insert); - $statement->execute([ - $snapshot->aggregateId(), - $snapshot->aggregateType(), - $snapshot->lastVersion(), - $snapshot->createdAt()->format('Y-m-d\TH:i:s.u'), - serialize($snapshot->aggregateRoot()), - ]); + $this->connection->beginTransaction(); + + foreach ($statements as $statement) { + $statement->execute(); + } $this->connection->commit(); } @@ -116,7 +140,7 @@ private function getTableName(string $aggregateType): string /** * @param string|resource $serialized - * @return object + * @return object|array */ private function unserializeAggregateRoot($serialized) { diff --git a/tests/PdoSnapshotStoreTest.php b/tests/PdoSnapshotStoreTest.php index be3e606..2180bdc 100644 --- a/tests/PdoSnapshotStoreTest.php +++ b/tests/PdoSnapshotStoreTest.php @@ -67,6 +67,41 @@ public function it_saves_and_reads() $this->assertCount(1, $snapshots); } + /** + * @test + */ + public function it_saves_multiple_snapshots() + { + $aggregateRoot1 = new \stdClass(); + $aggregateRoot1->foo = 'bar'; + + $aggregateRoot2 = ['foo' => 'baz']; + + $time = (string) microtime(true); + if (false === strpos($time, '.')) { + $time .= '.0000'; + } + + $now = \DateTimeImmutable::createFromFormat('U.u', $time); + + $snapshot1 = new Snapshot('object', 'id_one', $aggregateRoot1, 1, $now); + + $snapshot2 = new Snapshot('array', 'id_two', $aggregateRoot2, 2, $now); + + $this->snapshotStore->save($snapshot1, $snapshot2); + + $this->assertEquals($snapshot1, $this->snapshotStore->get('object', 'id_one')); + $this->assertEquals($snapshot2, $this->snapshotStore->get('array', 'id_two')); + } + + /** + * @test + */ + public function it_returns_early_when_no_snapshots_given() + { + $this->snapshotStore->save(); + } + /** * @test */ @@ -117,6 +152,14 @@ protected function setUp(): void $this->snapshotStore = new PdoSnapshotStore($this->connection, ['foo' => 'bar'], 'snapshots'); } + protected function tearDown(): void + { + $statement = $this->connection->prepare('TRUNCATE snapshots'); + $statement->execute(); + $statement = $this->connection->prepare('TRUNCATE bar'); + $statement->execute(); + } + protected function createTable(string $name) { switch (TestUtil::getDatabaseVendor()) {