Passed
Push — master ( 848a13...c93e13 )
by Mauro
02:28
created

PdoRepository::findListByUuid()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 3
nop 1
dl 0
loc 25
rs 8.8571
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\Domain\Model\ListElementUuid;
20
use InMemoryList\Infrastructure\Persistance\Exceptions\ListAlreadyExistsException;
21
use InMemoryList\Infrastructure\Persistance\Exceptions\ListDoesNotExistsException;
22
use Predis\Client;
23
24
class PdoRepository extends AbstractRepository implements ListRepositoryInterface
25
{
26
    const LIST_COLLECTION_TABLE_NAME = 'list_collection';
27
    const LIST_ELEMENT_TABLE_NAME = 'list_element';
28
29
    /**
30
     * @var \PDO
31
     */
32
    private $pdo;
33
34
    /**
35
     * PdoRepository constructor.
36
     * 
37
     * @param \PDO $pdo
38
     * @param bool $createSchema
39
     */
40
    public function __construct(\PDO $pdo, $createSchema = false)
41
    {
42
        $this->pdo = $pdo;
43
44
        if($createSchema){
45
            $this->createListCollectionSchema();
46
            $this->createListElementSchema();
47
        }
48
    }
49
50
    private function createListCollectionSchema()
51
    {
52
        $query = "CREATE TABLE IF NOT EXISTS `".self::LIST_COLLECTION_TABLE_NAME."` (
53
          `uuid` varchar(255) UNIQUE NOT NULL,
54
          `headers` text DEFAULT NULL,
55
          `created_at` TIMESTAMP NOT NULL,
56
          `updated_at` TIMESTAMP NOT NULL DEFAULT NOW() ON UPDATE NOW(),
57
          PRIMARY KEY (`uuid`)
58
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
59
60
        $this->pdo->exec($query);
61
    }
62
63
    private function createListElementSchema()
64
    {
65
        $query = "CREATE TABLE IF NOT EXISTS `".self::LIST_ELEMENT_TABLE_NAME."` (
66
          `uuid` varchar(255) UNIQUE NOT NULL,
67
          `list` varchar(255) NOT NULL,
68
          `body` text DEFAULT NULL,
69
          `created_at` TIMESTAMP NOT NULL,
70
          `updated_at` TIMESTAMP NOT NULL DEFAULT NOW() ON UPDATE NOW(),
71
          PRIMARY KEY (`uuid`),
72
          CONSTRAINT `list_foreign_key` FOREIGN KEY (`list`) REFERENCES `".self::LIST_COLLECTION_TABLE_NAME."`(`uuid`) ON UPDATE CASCADE ON DELETE CASCADE
73
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;";
74
75
        $this->pdo->exec($query);
76
    }
77
78
    /**
79
     * @param ListCollection $list
80
     * @param null           $ttl
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...
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...
81
     * @param null           $chunkSize
82
     *
83
     * @return mixed
84
     *
85
     * @throws ListAlreadyExistsException
86
     */
87
    public function create(ListCollection $list, $ttl = null, $chunkSize = null)
88
    {
89
        $sql = 'INSERT INTO `'.self::LIST_COLLECTION_TABLE_NAME.'` (
90
                    `uuid`,
91
                    `headers`,
92
                    `created_at`
93
                  ) VALUES (
94
                    :uuid,
95
                    :headers,
96
                    :created_at
97
            )';
98
99
        $data = [
100
            'uuid' => $list->getUuid(),
101
            'headers' => $list->getHeaders(),
102
            'created_at' => (new \DateTimeImmutable())->format('Y-m-d H:i:s.u')
103
        ];
104
105
        $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...
106
            'sql' => $sql,
107
            'data' => $data,
108
        ];
109
110
        foreach ($list->getElements() as $uuid => $element){
111
            /** @var ListElement $element */
112
113
            $sql = 'INSERT INTO `'.self::LIST_ELEMENT_TABLE_NAME.'` (
114
                    `uuid`,
115
                    `list`,
116
                    `body`
117
                  ) VALUES (
118
                    :uuid,
119
                    :list,
120
                    :body
121
            )';
122
123
            $data = [
124
                'uuid' => $uuid,
125
                'list' => $list->getUuid(),
126
                'body' => $element->getBody(),
127
            ];
128
129
            $st[] = [
130
                'sql' => $sql,
131
                'data' => $data,
132
            ];
133
        }
134
135
        $this->executeQueriesInATransaction($st);
136
    }
137
138
    /**
139
     * @param array $statements
140
     */
141
    private function executeQueriesInATransaction(array $statements)
142
    {
143
        try {
144
            $this->pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION);
145
146
            // beginTransaction
147
            $this->pdo->beginTransaction();
148
149
            foreach ($statements as $statement){
150
                if(isset($statement['sql'])){
151
152
                    $sql = $statement['sql'];
153
                    $data = isset($statement['data']) ? $statement['data'] : [];
154
155
                    $stmt = $this->pdo->prepare($sql);
156
                    if (!empty($data)) {
157
                        foreach ($data as $key => &$value){
158
                            if(is_array($value)){
159
                                $value = serialize($value);
160
                            }
161
162
                            $stmt->bindParam(':'.$key, $value);
163
                        }
164
                    }
165
                    $stmt->execute();
166
                }
167
            }
168
169
            // commit
170
            $this->pdo->commit();
171
        } catch(\PDOException $e){
172
            $this->pdo->rollBack();
173
            throw $e;
174
        }
175
    }
176
177
    /**
178
     * @param $listUuid
179
     * @param $elementUuid
180
     *
181
     * @return mixed
182
     */
183 View Code Duplication
    public function deleteElement($listUuid, $elementUuid)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
184
    {
185
        $sql = 'DELETE FROM `'.self::LIST_ELEMENT_TABLE_NAME.'` WHERE `uuid` = :uuid AND `list` = :list';
186
        $stmt = $this->pdo->prepare($sql);
187
        $stmt->bindParam(':uuid', $elementUuid);
188
        $stmt->bindParam(':list', $listUuid);
189
        $stmt->execute();
190
    }
191
192
    /**
193
     * @param $listUuid
194
     *
195
     * @return bool
196
     */
197
    public function exists($listUuid)
198
    {
199
        return ($this->getCounter($listUuid) > 0) ? true : false;
200
    }
201
202
    /**
203
     * @param $listUuid
204
     *
205
     * @return mixed
206
     */
207
    public function findListByUuid($listUuid)
208
    {
209
        $sql = 'SELECT 
210
                    `uuid`,
211
                    `list`,
212
                    `body`
213
                    FROM `'.self::LIST_ELEMENT_TABLE_NAME.'` 
214
                    WHERE `list` = :list';
215
        $stmt = $this->pdo->prepare($sql);
216
        $stmt->bindParam('list', $listUuid);
217
        $stmt->execute();
218
219
        $list = $stmt->fetchAll(
220
            \PDO::FETCH_ASSOC
221
        );
222
223
        if(count($list) === 0){
224
            return [];
225
        }
226
227
        foreach ($list as $item){
228
            $items[$item['uuid']] = unserialize($item['body']);
229
        }
230
231
        return $items;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $items seems to be defined by a foreach iteration on line 227. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
232
    }
233
234
    /**
235
     * @return mixed
236
     */
237
    public function flush()
238
    {
239
        $sql = 'DELETE FROM `'.self::LIST_COLLECTION_TABLE_NAME.'`';
240
        $stmt = $this->pdo->prepare($sql);
241
        $stmt->execute();
242
243
        $sql = 'DELETE FROM `'.self::LIST_ELEMENT_TABLE_NAME.'`';
244
        $stmt = $this->pdo->prepare($sql);
245
        $stmt->execute();
246
    }
247
248
    /**
249
     * @param $listUuid
250
     *
251
     * @return array
252
     */
253
    public function getHeaders($listUuid)
254
    {
255
        $sql = 'SELECT
256
                `headers`
257
                FROM `'.self::LIST_COLLECTION_TABLE_NAME.'` 
258
                WHERE `uuid` = :uuid';
259
260
        $stmt = $this->pdo->prepare($sql);
261
        $stmt->bindParam(':uuid', $listUuid);
262
        $stmt->execute();
263
264
        if($list = $stmt->fetchAll(\PDO::FETCH_ASSOC)){
265
            $headers = $list[0]['headers'];
266
267
            if (SerializeChecker::isSerialized($headers)) {
268
                return unserialize($headers);
269
            }
270
271
            return $headers;
272
        }
273
274
        return null;
275
    }
276
277
    /**
278
     * @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...
279
     *
280
     * @return array
281
     */
282
    public function getIndex($listUuid = null)
283
    {
284
        $lt = self::LIST_ELEMENT_TABLE_NAME;
285
        $lc = self::LIST_COLLECTION_TABLE_NAME;
286
287
        $sql = 'SELECT
288
                `'.$lc.'`.`uuid`,
289
                `'.$lc.'`.`headers`,
290
                `'.$lc.'`.`created_at`
291
                FROM `'.$lt.'` 
292
                JOIN `'.$lc.'` 
293
                ON `'.$lt.'`.`list` = `'.$lc.'`.`uuid`';
294
295
        if($listUuid){
296
            $sql .= 'WHERE `'.$lc.'`.`uuid` = :list';
297
        }
298
299
        $stmt = $this->pdo->prepare($sql);
300
        if($listUuid){
301
            $stmt->bindParam(':list', $listUuid);
302
        }
303
        $stmt->execute();
304
305
        $list = $stmt->fetchAll(\PDO::FETCH_ASSOC);
306
307
        foreach ($list as $item){
308
            $index[$item['uuid']] = [
0 ignored issues
show
Comprehensibility Best Practice introduced by
$index was never initialized. Although not strictly required by PHP, it is generally a good practice to add $index = array(); before regardless.
Loading history...
309
                'uuid' => $item['uuid'],
310
                'created_on' => \DateTime::createFromFormat('Y-m-d H:i:s.u', $item['created_at']),
311
                'size' => $stmt->rowCount(),
312
                'chunks' => 0,
313
                'chunk-size' => 0,
314
                'headers' => $this->getHeaders($item['uuid']),
315
                'ttl' => 0
316
            ];
317
318
            return $index;
319
        }
320
321
        return null;
322
    }
323
324
    /**
325
     * @return array
326
     */
327
    public function getStatistics()
328
    {
329
    }
330
331
    /**
332
     * @param $listUuid
333
     * @param ListElement $listElement
334
     *
335
     * @throws ListElementNotConsistentException
336
     *
337
     * @return mixed
338
     */
339
    public function pushElement($listUuid, ListElement $listElement)
340
    {
341
        $elementUuid = $listElement->getUuid();
342
        $body = $listElement->getBody();
343
344 View Code Duplication
        if (!ListElementConsistencyChecker::isConsistent($listElement, $this->findListByUuid($listUuid))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
345
            throw new ListElementNotConsistentException('Element '.(string) $listElement->getUuid().' is not consistent with list data.');
346
        }
347
348
        $sql = 'INSERT INTO `'.self::LIST_ELEMENT_TABLE_NAME.'` (
349
                    `uuid`,
350
                    `list`,
351
                    `body`
352
                  ) VALUES (
353
                    :uuid,
354
                    :list,
355
                    :body
356
            )';
357
358
        $stmt = $this->pdo->prepare($sql);
359
        $stmt->bindParam(':uuid', $elementUuid);
360
        $stmt->bindParam(':list', $listUuid);
361
        $stmt->bindParam(':body', $body);
362
        $stmt->execute();
363
    }
364
365
    /**
366
     * @param $listUuid
367
     *
368
     * @return mixed
369
     */
370
    public function removeListFromIndex($listUuid)
371
    {
372
    }
373
374
    /**
375
     * @param $listUuid
376
     * @param $elementUuid
377
     * @param array $data
378
     *
379
     * @throws ListElementNotConsistentException
380
     *
381
     * @return mixed
382
     */
383
    public function updateElement($listUuid, $elementUuid, $data)
384
    {
385
        $listElement = $this->findElement(
386
            (string) $listUuid,
387
            (string) $elementUuid
388
        );
389
390
        $updatedElementBody = $this->updateListElementBody($listElement, $data);
391
392
        if (!ListElementConsistencyChecker::isConsistent($updatedElementBody, $this->findListByUuid($listUuid))) {
393
            throw new ListElementNotConsistentException('Element '.(string) $elementUuid.' is not consistent with list data.');
394
        }
395
396
        if(is_array($updatedElementBody) || is_object($updatedElementBody)){
397
            $updatedElementBody = serialize($updatedElementBody);
398
        }
399
400
        $sql = 'UPDATE `'.self::LIST_ELEMENT_TABLE_NAME.'` 
401
                    SET `body` = :body
402
                    WHERE `uuid` = :uuid';
403
404
        $stmt = $this->pdo->prepare($sql);
405
        $stmt->bindParam(':uuid', $elementUuid);
406
        $stmt->bindParam(':body', $updatedElementBody);
407
        $stmt->execute();
408
    }
409
410
    /**
411
     * @param $listUuid
412
     * @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...
413
     *
414
     * @return mixed
415
     *
416
     * @throws ListDoesNotExistsException
417
     */
418
    public function updateTtl($listUuid, $ttl)
419
    {
420
    }
421
422
    /**
423
     * @param $listUuid
424
     *
425
     * @return int
426
     */
427 View Code Duplication
    public function getCounter($listUuid)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
428
    {
429
        $sql = 'SELECT `uuid` FROM `'.self::LIST_ELEMENT_TABLE_NAME.'` WHERE `list` = :list';
430
        $stmt = $this->pdo->prepare($sql);
431
        $stmt->bindParam(':list', $listUuid);
432
        $stmt->execute();
433
434
        return $stmt->rowCount();
435
    }
436
}
437