Passed
Push — master ( b66278...af918b )
by Mauro
01:50
created

PdoRepository::deleteElement()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 7
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 2
dl 7
loc 7
rs 9.4285
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 $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...
45
     * @param null           $chunkSize
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
            )';
62
63
        $data = [
64
            'uuid' => $list->getUuid(),
65
            'headers' => $list->getHeaders(),
66
            'created_at' => (new \DateTimeImmutable())->format('Y-m-d H:i:s.u'),
67
        ];
68
69
        $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...
70
            'sql' => $sql,
71
            'data' => $data,
72
        ];
73
74
        foreach ($list->getElements() as $uuid => $element) {
75
            /** @var ListElement $element */
76
            $sql = 'INSERT INTO `'.self::LIST_ELEMENT_TABLE_NAME.'` (
77
                    `uuid`,
78
                    `list`,
79
                    `body`
80
                  ) VALUES (
81
                    :uuid,
82
                    :list,
83
                    :body
84
            )';
85
86
            $data = [
87
                'uuid' => $uuid,
88
                'list' => $list->getUuid(),
89
                'body' => $element->getBody(),
90
            ];
91
92
            $st[] = [
93
                'sql' => $sql,
94
                'data' => $data,
95
            ];
96
        }
97
98
        $this->executeQueriesInATransaction($st);
99
    }
100
101
    /**
102
     * @param array $statements
103
     */
104
    private function executeQueriesInATransaction(array $statements)
105
    {
106
        try {
107
            $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
108
109
            // beginTransaction
110
            $this->pdo->beginTransaction();
111
112
            foreach ($statements as $statement) {
113
                if (isset($statement['sql'])) {
114
                    $sql = $statement['sql'];
115
                    $data = isset($statement['data']) ? $statement['data'] : [];
116
117
                    $stmt = $this->pdo->prepare($sql);
118
                    if (!empty($data)) {
119
                        foreach ($data as $key => &$value) {
120
                            if (is_array($value)) {
121
                                $value = serialize($value);
122
                            }
123
124
                            $stmt->bindParam(':'.$key, $value);
125
                        }
126
                    }
127
                    $stmt->execute();
128
                }
129
            }
130
131
            // commit
132
            $this->pdo->commit();
133
        } catch (\PDOException $e) {
134
            $this->pdo->rollBack();
135
            throw $e;
136
        }
137
    }
138
139
    /**
140
     * @param $listUuid
141
     * @param $elementUuid
142
     *
143
     * @return mixed
144
     */
145 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...
146
    {
147
        $sql = 'DELETE FROM `'.self::LIST_ELEMENT_TABLE_NAME.'` WHERE `uuid` = :uuid AND `list` = :list';
148
        $stmt = $this->pdo->prepare($sql);
149
        $stmt->bindParam(':uuid', $elementUuid);
150
        $stmt->bindParam(':list', $listUuid);
151
        $stmt->execute();
152
    }
153
154
    /**
155
     * @param $listUuid
156
     *
157
     * @return bool
158
     */
159
    public function exists($listUuid)
160
    {
161
        return ($this->getCounter($listUuid) > 0) ? true : false;
162
    }
163
164
    /**
165
     * @param $listUuid
166
     *
167
     * @return mixed
168
     */
169
    public function findListByUuid($listUuid)
170
    {
171
        $sql = 'SELECT 
172
                    `uuid`,
173
                    `list`,
174
                    `body`
175
                    FROM `'.self::LIST_ELEMENT_TABLE_NAME.'` 
176
                    WHERE `list` = :list';
177
        $stmt = $this->pdo->prepare($sql);
178
        $stmt->bindParam('list', $listUuid);
179
        $stmt->execute();
180
181
        $list = $stmt->fetchAll(
182
            \PDO::FETCH_ASSOC
183
        );
184
185
        if (count($list) === 0) {
186
            return [];
187
        }
188
189
        foreach ($list as $item) {
190
            $items[$item['uuid']] = unserialize($item['body']);
191
        }
192
193
        return $items;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $items seems to be defined by a foreach iteration on line 189. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
194
    }
195
196
    /**
197
     * @return mixed
198
     */
199
    public function flush()
200
    {
201
        $sql = 'DELETE FROM `'.self::LIST_COLLECTION_TABLE_NAME.'`';
202
        $stmt = $this->pdo->prepare($sql);
203
        $stmt->execute();
204
205
        $sql = 'DELETE FROM `'.self::LIST_ELEMENT_TABLE_NAME.'`';
206
        $stmt = $this->pdo->prepare($sql);
207
        $stmt->execute();
208
    }
209
210
    /**
211
     * @param $listUuid
212
     *
213
     * @return array
214
     */
215
    public function getHeaders($listUuid)
216
    {
217
        $sql = 'SELECT
218
                `headers`
219
                FROM `'.self::LIST_COLLECTION_TABLE_NAME.'` 
220
                WHERE `uuid` = :uuid';
221
222
        $stmt = $this->pdo->prepare($sql);
223
        $stmt->bindParam(':uuid', $listUuid);
224
        $stmt->execute();
225
226
        if ($list = $stmt->fetchAll(\PDO::FETCH_ASSOC)) {
227
            $headers = $list[0]['headers'];
228
229
            if (SerializeChecker::isSerialized($headers)) {
230
                return unserialize($headers);
231
            }
232
233
            return $headers;
234
        }
235
236
        return null;
237
    }
238
239
    /**
240
     * @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...
241
     *
242
     * @return array
243
     */
244
    public function getIndex($listUuid = null)
245
    {
246
        $lt = self::LIST_ELEMENT_TABLE_NAME;
247
        $lc = self::LIST_COLLECTION_TABLE_NAME;
248
249
        $sql = 'SELECT
250
                `'.$lc.'`.`uuid`,
251
                `'.$lc.'`.`headers`,
252
                `'.$lc.'`.`created_at`
253
                FROM `'.$lt.'` 
254
                JOIN `'.$lc.'` 
255
                ON `'.$lt.'`.`list` = `'.$lc.'`.`uuid`';
256
257
        if ($listUuid) {
258
            $sql .= 'WHERE `'.$lc.'`.`uuid` = :list';
259
        }
260
261
        $stmt = $this->pdo->prepare($sql);
262
        if ($listUuid) {
263
            $stmt->bindParam(':list', $listUuid);
264
        }
265
        $stmt->execute();
266
267
        $list = $stmt->fetchAll(\PDO::FETCH_ASSOC);
268
269
        foreach ($list as $item) {
270
            $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...
271
                'uuid' => $item['uuid'],
272
                'created_on' => \DateTime::createFromFormat('Y-m-d H:i:s.u', $item['created_at']),
273
                'size' => $stmt->rowCount(),
274
                'chunks' => 0,
275
                'chunk-size' => 0,
276
                'headers' => $this->getHeaders($item['uuid']),
277
                'ttl' => 0,
278
            ];
279
280
            return $index;
281
        }
282
283
        return null;
284
    }
285
286
    /**
287
     * @return array
288
     */
289
    public function getStatistics()
290
    {
291
    }
292
293
    /**
294
     * @param $listUuid
295
     * @param ListElement $listElement
296
     *
297
     * @throws ListElementNotConsistentException
298
     *
299
     * @return mixed
300
     */
301
    public function pushElement($listUuid, ListElement $listElement)
302
    {
303
        $elementUuid = $listElement->getUuid();
304
        $body = $listElement->getBody();
305
306 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...
307
            throw new ListElementNotConsistentException('Element '.(string) $listElement->getUuid().' is not consistent with list data.');
308
        }
309
310
        $sql = 'INSERT INTO `'.self::LIST_ELEMENT_TABLE_NAME.'` (
311
                    `uuid`,
312
                    `list`,
313
                    `body`
314
                  ) VALUES (
315
                    :uuid,
316
                    :list,
317
                    :body
318
            )';
319
320
        $stmt = $this->pdo->prepare($sql);
321
        $stmt->bindParam(':uuid', $elementUuid);
322
        $stmt->bindParam(':list', $listUuid);
323
        $stmt->bindParam(':body', $body);
324
        $stmt->execute();
325
    }
326
327
    /**
328
     * @param $listUuid
329
     *
330
     * @return mixed
331
     */
332
    public function removeListFromIndex($listUuid)
333
    {
334
    }
335
336
    /**
337
     * @param $listUuid
338
     * @param $elementUuid
339
     * @param array $data
340
     *
341
     * @throws ListElementNotConsistentException
342
     *
343
     * @return mixed
344
     */
345
    public function updateElement($listUuid, $elementUuid, $data)
346
    {
347
        $listElement = $this->findElement(
348
            (string) $listUuid,
349
            (string) $elementUuid
350
        );
351
352
        $updatedElementBody = $this->updateListElementBody($listElement, $data);
353
354
        if (!ListElementConsistencyChecker::isConsistent($updatedElementBody, $this->findListByUuid($listUuid))) {
355
            throw new ListElementNotConsistentException('Element '.(string) $elementUuid.' is not consistent with list data.');
356
        }
357
358
        if (is_array($updatedElementBody) || is_object($updatedElementBody)) {
359
            $updatedElementBody = serialize($updatedElementBody);
360
        }
361
362
        $sql = 'UPDATE `'.self::LIST_ELEMENT_TABLE_NAME.'` 
363
                    SET `body` = :body
364
                    WHERE `uuid` = :uuid';
365
366
        $stmt = $this->pdo->prepare($sql);
367
        $stmt->bindParam(':uuid', $elementUuid);
368
        $stmt->bindParam(':body', $updatedElementBody);
369
        $stmt->execute();
370
    }
371
372
    /**
373
     * @param $listUuid
374
     * @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...
375
     *
376
     * @return mixed
377
     *
378
     * @throws ListDoesNotExistsException
379
     */
380
    public function updateTtl($listUuid, $ttl)
381
    {
382
    }
383
384
    /**
385
     * @param $listUuid
386
     *
387
     * @return int
388
     */
389 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...
390
    {
391
        $sql = 'SELECT `uuid` FROM `'.self::LIST_ELEMENT_TABLE_NAME.'` WHERE `list` = :list';
392
        $stmt = $this->pdo->prepare($sql);
393
        $stmt->bindParam(':list', $listUuid);
394
        $stmt->execute();
395
396
        return $stmt->rowCount();
397
    }
398
399
    /**
400
     * creates database schema.
401
     */
402 View Code Duplication
    public function createSchema()
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...
403
    {
404
        $query = 'CREATE TABLE IF NOT EXISTS `'.self::LIST_COLLECTION_TABLE_NAME.'` (
405
          `id` int NOT NULL AUTO_INCREMENT,
406
          `uuid` varchar(255) UNIQUE NOT NULL,
407
          `headers` text DEFAULT NULL,
408
          `created_at` TIMESTAMP NOT NULL,
409
          `updated_at` TIMESTAMP NOT NULL DEFAULT NOW() ON UPDATE NOW(),
410
          PRIMARY KEY (`id`)
411
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;';
412
413
        $this->pdo->exec($query);
414
415
        $query2 = 'CREATE TABLE IF NOT EXISTS `'.self::LIST_ELEMENT_TABLE_NAME.'` (
416
          `id` int NOT NULL AUTO_INCREMENT,
417
          `uuid` varchar(255) NOT NULL,
418
          `list` varchar(255) NOT NULL,
419
          `body` text DEFAULT NULL,
420
          `created_at` TIMESTAMP NOT NULL,
421
          `updated_at` TIMESTAMP NOT NULL DEFAULT NOW() ON UPDATE NOW(),
422
          PRIMARY KEY (`id`),
423
          CONSTRAINT `list_foreign_key` FOREIGN KEY (`list`) REFERENCES `'.self::LIST_COLLECTION_TABLE_NAME.'`(`uuid`) ON UPDATE CASCADE ON DELETE CASCADE
424
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;';
425
426
        $this->pdo->exec($query2);
427
    }
428
429
    /**
430
     * destroys database schema.
431
     */
432 View Code Duplication
    public function destroySchema()
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...
433
    {
434
        $query = 'DROP TABLE IF EXISTS `'.self::LIST_COLLECTION_TABLE_NAME.'`';
435
436
        $this->pdo->exec($query);
437
438
        $query2 = 'DROP TABLE IF EXISTS `'.self::LIST_ELEMENT_TABLE_NAME.'`';
439
440
        $this->pdo->exec($query2);
441
    }
442
}
443