PdoRepository::executeQueriesInATransaction()   B
last analyzed

Complexity

Conditions 8
Paths 53

Size

Total Lines 32
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 18
nc 53
nop 1
dl 0
loc 32
rs 8.4444
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of the Simple EventStore Manager package.
4
 *
5
 * (c) Mauro Cassani<https://github.com/mauretto78>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace InMemoryList\Infrastructure\Persistance;
12
13
use InMemoryList\Domain\Helper\ListElementConsistencyChecker;
14
use InMemoryList\Domain\Helper\SerializeChecker;
15
use InMemoryList\Domain\Model\Contracts\ListRepositoryInterface;
16
use InMemoryList\Domain\Model\Exceptions\ListElementNotConsistentException;
17
use InMemoryList\Domain\Model\ListCollection;
18
use InMemoryList\Domain\Model\ListElement;
19
use InMemoryList\Infrastructure\Persistance\Exceptions\ListAlreadyExistsException;
20
use InMemoryList\Infrastructure\Persistance\Exceptions\ListDoesNotExistsException;
21
22
class PdoRepository extends AbstractRepository implements ListRepositoryInterface
23
{
24
    const LIST_COLLECTION_TABLE_NAME = 'list_collection';
25
    const LIST_ELEMENT_TABLE_NAME = 'list_element';
26
27
    /**
28
     * @var \PDO
29
     */
30
    private $pdo;
31
32
    /**
33
     * PdoRepository constructor.
34
     *
35
     * @param \PDO $pdo
36
     */
37
    public function __construct(\PDO $pdo)
38
    {
39
        $this->pdo = $pdo;
40
    }
41
42
    /**
43
     * @param ListCollection $list
44
     * @param null           $ttl
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $ttl is correct as it would always require null to be passed?
Loading history...
45
     * @param null           $chunkSize
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $chunkSize is correct as it would always require null to be passed?
Loading history...
46
     *
47
     * @return mixed
48
     *
49
     * @throws ListAlreadyExistsException
50
     */
51
    public function create(ListCollection $list, $ttl = null, $chunkSize = null)
52
    {
53
        $sql = 'INSERT INTO `'.self::LIST_COLLECTION_TABLE_NAME.'` (
54
                    `uuid`,
55
                    `headers`,
56
                    `created_at`
57
                  ) VALUES (
58
                    :uuid,
59
                    :headers,
60
                    :created_at
61
            ) ON DUPLICATE KEY UPDATE 
62
                `headers` = :headers,
63
                `created_at` = :created_at
64
            ';
65
66
        $data = [
67
            'uuid' => $list->getUuid(),
68
            'headers' => $list->getHeaders(),
69
            'created_at' => (new \DateTimeImmutable())->format('Y-m-d H:i:s.u'),
70
        ];
71
72
        $st[] = [
0 ignored issues
show
Comprehensibility Best Practice introduced by
$st was never initialized. Although not strictly required by PHP, it is generally a good practice to add $st = array(); before regardless.
Loading history...
73
            'sql' => $sql,
74
            'data' => $data,
75
        ];
76
77
        foreach ($list->getElements() as $uuid => $element) {
78
            /** @var ListElement $element */
79
            $sql = 'INSERT INTO `'.self::LIST_ELEMENT_TABLE_NAME.'` (
80
                    `uuid`,
81
                    `list`,
82
                    `body`
83
                  ) VALUES (
84
                    :uuid,
85
                    :list,
86
                    :body
87
            ) ON DUPLICATE KEY UPDATE 
88
                `list` = :list,
89
                `body` = :body
90
            ';
91
92
            $data = [
93
                'uuid' => $uuid,
94
                'list' => $list->getUuid(),
95
                'body' => $element->getBody(),
96
            ];
97
98
            $st[] = [
99
                'sql' => $sql,
100
                'data' => $data,
101
            ];
102
        }
103
104
        $this->executeQueriesInATransaction($st);
105
    }
106
107
    /**
108
     * @param array $statements
109
     */
110
    private function executeQueriesInATransaction(array $statements)
111
    {
112
        try {
113
            $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
114
115
            // beginTransaction
116
            $this->pdo->beginTransaction();
117
118
            foreach ($statements as $statement) {
119
                if (isset($statement['sql'])) {
120
                    $sql = $statement['sql'];
121
                    $data = isset($statement['data']) ? $statement['data'] : [];
122
123
                    $stmt = $this->pdo->prepare($sql);
124
                    if (!empty($data)) {
125
                        foreach ($data as $key => &$value) {
126
                            if (is_array($value)) {
127
                                $value = serialize($value);
128
                            }
129
130
                            $stmt->bindParam(':'.$key, $value);
131
                        }
132
                    }
133
                    $stmt->execute();
134
                }
135
            }
136
137
            // commit
138
            $this->pdo->commit();
139
        } catch (\PDOException $e) {
140
            $this->pdo->rollBack();
141
            throw $e;
142
        }
143
    }
144
145
    /**
146
     * @param $listUuid
147
     * @param $elementUuid
148
     *
149
     * @return mixed
150
     */
151
    public function deleteElement($listUuid, $elementUuid)
152
    {
153
        $sql = 'DELETE FROM `'.self::LIST_ELEMENT_TABLE_NAME.'` WHERE `uuid` = :uuid AND `list` = :list';
154
        $stmt = $this->pdo->prepare($sql);
155
        $stmt->bindParam(':uuid', $elementUuid);
156
        $stmt->bindParam(':list', $listUuid);
157
        $stmt->execute();
158
    }
159
160
    /**
161
     * @param $listUuid
162
     *
163
     * @return bool
164
     */
165
    public function exists($listUuid)
166
    {
167
        return ($this->getCounter($listUuid) > 0) ? true : false;
168
    }
169
170
    /**
171
     * @param $listUuid
172
     *
173
     * @return mixed
174
     */
175
    public function findListByUuid($listUuid)
176
    {
177
        $sql = 'SELECT 
178
                    `uuid`,
179
                    `list`,
180
                    `body`
181
                    FROM `'.self::LIST_ELEMENT_TABLE_NAME.'` 
182
                    WHERE `list` = :list';
183
        $stmt = $this->pdo->prepare($sql);
184
        $stmt->bindParam('list', $listUuid);
185
        $stmt->execute();
186
187
        $list = $stmt->fetchAll(
188
            \PDO::FETCH_ASSOC
189
        );
190
191
        if (count($list) === 0) {
192
            return [];
193
        }
194
195
        foreach ($list as $item) {
196
            $items[$item['uuid']] = unserialize($item['body']);
197
        }
198
199
        return $items;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $items seems to be defined by a foreach iteration on line 195. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
200
    }
201
202
    /**
203
     * @return mixed
204
     */
205
    public function flush()
206
    {
207
        $sql = 'DELETE FROM `'.self::LIST_COLLECTION_TABLE_NAME.'`';
208
        $stmt = $this->pdo->prepare($sql);
209
        $stmt->execute();
210
211
        $sql = 'DELETE FROM `'.self::LIST_ELEMENT_TABLE_NAME.'`';
212
        $stmt = $this->pdo->prepare($sql);
213
        $stmt->execute();
214
    }
215
216
    /**
217
     * @param $listUuid
218
     *
219
     * @return array
220
     */
221
    public function getHeaders($listUuid)
222
    {
223
        $sql = 'SELECT
224
                `headers`
225
                FROM `'.self::LIST_COLLECTION_TABLE_NAME.'` 
226
                WHERE `uuid` = :uuid';
227
228
        $stmt = $this->pdo->prepare($sql);
229
        $stmt->bindParam(':uuid', $listUuid);
230
        $stmt->execute();
231
232
        if ($list = $stmt->fetchAll(\PDO::FETCH_ASSOC)) {
233
            $headers = $list[0]['headers'];
234
235
            if (SerializeChecker::isSerialized($headers)) {
236
                return unserialize($headers);
237
            }
238
239
            return $headers;
240
        }
241
242
        return null;
243
    }
244
245
    /**
246
     * @param null $listUuid
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $listUuid is correct as it would always require null to be passed?
Loading history...
247
     *
248
     * @return array
249
     */
250
    public function getIndex($listUuid = null)
251
    {
252
        $lt = self::LIST_ELEMENT_TABLE_NAME;
253
        $lc = self::LIST_COLLECTION_TABLE_NAME;
254
255
        $sql = 'SELECT
256
                `'.$lc.'`.`uuid`,
257
                `'.$lc.'`.`headers`,
258
                `'.$lc.'`.`created_at`
259
                FROM `'.$lt.'` 
260
                INNER JOIN `'.$lc.'` 
261
                ON `'.$lt.'`.`list` = `'.$lc.'`.`uuid`';
262
263
        if ($listUuid) {
0 ignored issues
show
introduced by
$listUuid is of type null, thus it always evaluated to false.
Loading history...
264
            $sql .= 'WHERE `'.$lc.'`.`uuid` = :list';
265
        }
266
267
        $stmt = $this->pdo->prepare($sql);
268
        if ($listUuid) {
0 ignored issues
show
introduced by
$listUuid is of type null, thus it always evaluated to false.
Loading history...
269
            $stmt->bindParam(':list', $listUuid);
270
        }
271
        $stmt->execute();
272
273
        $list = $stmt->fetchAll(\PDO::FETCH_ASSOC);
274
275
        $index = [];
276
277
        foreach ($list as $item) {
278
            $index[$item['uuid']] = [
279
                'uuid' => $item['uuid'],
280
                'created_on' => \DateTime::createFromFormat('Y-m-d H:i:s.u', $item['created_at']),
281
                'size' => $this->getCounter($item['uuid']),
282
                'chunks' => 0,
283
                'chunk-size' => 0,
284
                'headers' => $this->getHeaders($item['uuid']),
285
                'ttl' => 0,
286
            ];
287
        }
288
289
        return $index;
290
    }
291
292
    /**
293
     * @return array
294
     */
295
    public function getStatistics()
296
    {
297
    }
298
299
    /**
300
     * @param $listUuid
301
     * @param ListElement $listElement
302
     *
303
     * @throws ListElementNotConsistentException
304
     *
305
     * @return mixed
306
     */
307
    public function pushElement($listUuid, ListElement $listElement)
308
    {
309
        $elementUuid = $listElement->getUuid();
310
        $body = $listElement->getBody();
311
312
        if (!ListElementConsistencyChecker::isConsistent($listElement, $this->findListByUuid($listUuid))) {
313
            throw new ListElementNotConsistentException('Element '.(string) $listElement->getUuid().' is not consistent with list data.');
314
        }
315
316
        $sql = 'INSERT INTO `'.self::LIST_ELEMENT_TABLE_NAME.'` (
317
                    `uuid`,
318
                    `list`,
319
                    `body`
320
                  ) VALUES (
321
                    :uuid,
322
                    :list,
323
                    :body
324
            ) ON DUPLICATE KEY UPDATE 
325
                `list` = :list,
326
                `body` = :body
327
            ';
328
329
        $stmt = $this->pdo->prepare($sql);
330
        $stmt->bindParam(':uuid', $elementUuid);
331
        $stmt->bindParam(':list', $listUuid);
332
        $stmt->bindParam(':body', $body);
333
        $stmt->execute();
334
    }
335
336
    /**
337
     * @param $listUuid
338
     *
339
     * @return mixed
340
     */
341
    public function removeListFromIndex($listUuid)
342
    {
343
    }
344
345
    /**
346
     * @param $listUuid
347
     * @param $elementUuid
348
     * @param array $data
349
     *
350
     * @throws ListElementNotConsistentException
351
     *
352
     * @return mixed
353
     */
354
    public function updateElement($listUuid, $elementUuid, $data)
355
    {
356
        $listElement = $this->findElement(
357
            (string) $listUuid,
358
            (string) $elementUuid
359
        );
360
361
        $updatedElementBody = $this->updateListElementBody($listElement, $data);
362
363
        if (!ListElementConsistencyChecker::isConsistent($updatedElementBody, $this->findListByUuid($listUuid))) {
364
            throw new ListElementNotConsistentException('Element '.(string) $elementUuid.' is not consistent with list data.');
365
        }
366
367
        if (is_array($updatedElementBody) || is_object($updatedElementBody)) {
368
            $updatedElementBody = serialize($updatedElementBody);
369
        }
370
371
        $sql = 'UPDATE `'.self::LIST_ELEMENT_TABLE_NAME.'` 
372
                    SET `body` = :body
373
                    WHERE `uuid` = :uuid';
374
375
        $stmt = $this->pdo->prepare($sql);
376
        $stmt->bindParam(':uuid', $elementUuid);
377
        $stmt->bindParam(':body', $updatedElementBody);
378
        $stmt->execute();
379
    }
380
381
    /**
382
     * @param $listUuid
383
     * @param null $ttl
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $ttl is correct as it would always require null to be passed?
Loading history...
384
     *
385
     * @return mixed
386
     *
387
     * @throws ListDoesNotExistsException
388
     */
389
    public function updateTtl($listUuid, $ttl)
390
    {
391
    }
392
393
    /**
394
     * @param $listUuid
395
     *
396
     * @return int
397
     */
398
    public function getCounter($listUuid)
399
    {
400
        $sql = 'SELECT `uuid` FROM `'.self::LIST_ELEMENT_TABLE_NAME.'` WHERE `list` = :list';
401
        $stmt = $this->pdo->prepare($sql);
402
        $stmt->bindParam(':list', $listUuid);
403
        $stmt->execute();
404
405
        return $stmt->rowCount();
406
    }
407
408
    /**
409
     * creates database schema.
410
     */
411
    public function createSchema()
412
    {
413
        $query = 'CREATE TABLE IF NOT EXISTS `'.self::LIST_COLLECTION_TABLE_NAME.'` (
414
          `id` int NOT NULL AUTO_INCREMENT,
415
          `uuid` varchar(255) UNIQUE NOT NULL,
416
          `headers` text DEFAULT NULL,
417
          `created_at` TIMESTAMP NOT NULL,
418
          `updated_at` TIMESTAMP NOT NULL DEFAULT NOW() ON UPDATE NOW(),
419
          PRIMARY KEY (`id`)
420
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;';
421
422
        $this->pdo->exec($query);
423
424
        $query2 = 'CREATE TABLE IF NOT EXISTS `'.self::LIST_ELEMENT_TABLE_NAME.'` (
425
          `id` int NOT NULL AUTO_INCREMENT,
426
          `uuid` varchar(255) NOT NULL,
427
          `list` varchar(255) NOT NULL,
428
          `body` text DEFAULT NULL,
429
          `created_at` TIMESTAMP NOT NULL,
430
          `updated_at` TIMESTAMP NOT NULL DEFAULT NOW() ON UPDATE NOW(),
431
          PRIMARY KEY (`id`),
432
          CONSTRAINT `list_foreign_key` FOREIGN KEY (`list`) REFERENCES `'.self::LIST_COLLECTION_TABLE_NAME.'`(`uuid`) ON UPDATE CASCADE ON DELETE CASCADE
433
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;';
434
435
        $this->pdo->exec($query2);
436
    }
437
438
    /**
439
     * destroys database schema.
440
     */
441
    public function destroySchema()
442
    {
443
        $query = 'DROP TABLE IF EXISTS `'.self::LIST_COLLECTION_TABLE_NAME.'`';
444
445
        $this->pdo->exec($query);
446
447
        $query2 = 'DROP TABLE IF EXISTS `'.self::LIST_ELEMENT_TABLE_NAME.'`';
448
449
        $this->pdo->exec($query2);
450
    }
451
}
452