SqlSnapshotStore::load()   A
last analyzed

Complexity

Conditions 4
Paths 1

Size

Total Lines 48
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

Changes 6
Bugs 1 Features 0
Metric Value
cc 4
eloc 16
c 6
b 1
f 0
nc 1
nop 1
dl 0
loc 48
ccs 15
cts 15
cp 1
crap 4
rs 9.7333
1
<?php
2
3
/**
4
 * Event Sourcing implementation.
5
 *
6
 * @author  Maksim Masiukevich <[email protected]>
7
 * @license MIT
8
 * @license https://opensource.org/licenses/MIT
9
 */
10
11
declare(strict_types = 1);
12
13
namespace ServiceBus\EventSourcing\Snapshots\Store;
14
15
use function Amp\call;
16
use function ServiceBus\Storage\Sql\equalsCriteria;
17
use function ServiceBus\Storage\Sql\fetchOne;
18
use function ServiceBus\Storage\Sql\find;
19
use function ServiceBus\Storage\Sql\insertQuery;
20
use function ServiceBus\Storage\Sql\remove;
21
use Amp\Promise;
22
use ServiceBus\EventSourcing\AggregateId;
23
use ServiceBus\EventSourcing\Snapshots\Snapshot;
24
use ServiceBus\Storage\Common\BinaryDataDecoder;
25
use ServiceBus\Storage\Common\DatabaseAdapter;
26
27
/**
28
 *
29
 */
30
final class SqlSnapshotStore implements SnapshotStore
31
{
32
    private const TABLE_NAME = 'event_store_snapshots';
33
34
    /** @var DatabaseAdapter */
35
    private $adapter;
36
37 14
    public function __construct(DatabaseAdapter $adapter)
38
    {
39 14
        $this->adapter = $adapter;
40 14
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45 7
    public function save(Snapshot $snapshot): Promise
46
    {
47 7
        return call(
48
            function() use ($snapshot): \Generator
49
            {
50 7
                $insertQuery = insertQuery(self::TABLE_NAME, [
51 7
                    'id'                 => $snapshot->aggregate->id()->toString(),
52 7
                    'aggregate_id_class' => \get_class($snapshot->aggregate->id()),
53 7
                    'aggregate_class'    => \get_class($snapshot->aggregate),
54 7
                    'version'            => $snapshot->aggregate->version(),
55 7
                    'payload'            => \base64_encode(\serialize($snapshot)),
56 7
                    'created_at'         => $snapshot->aggregate->getCreatedAt()->format('Y-m-d H:i:s.u'),
57
                ]);
58
59 7
                $compiledQuery = $insertQuery->compile();
60
61
                /** @psalm-suppress MixedTypeCoercion Invalid params() docblock */
62 7
                yield $this->adapter->execute($compiledQuery->sql(), $compiledQuery->params());
63 7
            }
64
        );
65
    }
66
67
    /**
68
     * {@inheritdoc}
69
     */
70 7
    public function load(AggregateId $id): Promise
71
    {
72 7
        return call(
73
            function() use ($id): \Generator
74
            {
75 7
                $storedSnapshot = null;
76
77
                $criteria = [
78 7
                    equalsCriteria('id', $id->toString()),
79 7
                    equalsCriteria('aggregate_id_class', \get_class($id)),
80
                ];
81
82
                /** @var \ServiceBus\Storage\Common\ResultSet $resultSet */
83 7
                $resultSet = yield find($this->adapter, self::TABLE_NAME, $criteria);
84
85
                /**
86
                 * @psalm-var      array{
87
                 *   id: string,
88
                 *   aggregate_id_class: string,
89
                 *   aggregate_class: string,
90
                 *   version: int,
91
                 *   payload:string,
92
                 *   created_at: string
93
                 * }|null $data
94
                 *
95
                 * @var array<string, string>|null $data
96
                 */
97 7
                $data = yield fetchOne($resultSet);
98
99 7
                if (null !== $data)
100
                {
101 5
                    $payload = $data['payload'];
102
103 5
                    if ($this->adapter instanceof BinaryDataDecoder)
0 ignored issues
show
introduced by
$this->adapter is always a sub-type of ServiceBus\Storage\Common\BinaryDataDecoder.
Loading history...
104
                    {
105 5
                        $payload = $this->adapter->unescapeBinary($payload);
106
                    }
107
108 5
                    $snapshotContent = (string) \base64_decode($payload);
109
110 5
                    if ('' !== $snapshotContent)
111
                    {
112
                        /** @var Snapshot $storedSnapshot */
113 5
                        $storedSnapshot = \unserialize($snapshotContent, ['allowed_classes' => true]);
114
                    }
115
                }
116
117 7
                return $storedSnapshot;
118 7
            }
119
        );
120
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125 7
    public function remove(AggregateId $id): Promise
126
    {
127 7
        return call(
128
            function() use ($id): \Generator
129
            {
130
                $criteria = [
131 7
                    equalsCriteria('id', $id->toString()),
132 7
                    equalsCriteria('aggregate_id_class', \get_class($id)),
133
                ];
134
135 7
                yield remove($this->adapter, self::TABLE_NAME, $criteria);
136 7
            }
137
        );
138
    }
139
}
140