Passed
Push — master ( e7cae8...5047e3 )
by Peter
02:30
created

PageSqlDataMapper::loadCategory()   B

Complexity

Conditions 7
Paths 16

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 6
nc 16
nop 1
dl 0
loc 11
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AbterPhp\Website\Orm\DataMappers;
6
7
use AbterPhp\Website\Domain\Entities\Page as Entity;
8
use AbterPhp\Website\Domain\Entities\Page\Assets as PageAssets;
9
use AbterPhp\Website\Domain\Entities\PageCategory;
10
use AbterPhp\Website\Domain\Entities\PageLayout\Assets as LayoutAssets;
11
use Opulence\Orm\DataMappers\SqlDataMapper;
12
use Opulence\QueryBuilders\MySql\QueryBuilder;
13
use Opulence\QueryBuilders\MySql\SelectQuery;
14
15
class PageSqlDataMapper extends SqlDataMapper implements IPageDataMapper
16
{
17
    /**
18
     * @param Entity $entity
19
     */
20
    public function add($entity)
21
    {
22
        if (!($entity instanceof Entity)) {
0 ignored issues
show
introduced by
$entity is always a sub-type of AbterPhp\Website\Domain\Entities\Page.
Loading history...
23
            throw new \InvalidArgumentException(__CLASS__ . ':' . __FUNCTION__ . ' expects a Page entity.');
24
        }
25
26
        $data  = $this->getColumnNamesToValues($entity, true);
27
        $query = (new QueryBuilder())->insert('pages', $data);
28
29
        $sql    = $query->getSql();
30
        $params = $query->getParameters();
31
32
        $statement = $this->writeConnection->prepare($sql);
33
        $statement->bindValues($params);
34
        $statement->execute();
35
    }
36
37
    /**
38
     * @param Entity $entity
39
     */
40
    public function delete($entity)
41
    {
42
        if (!($entity instanceof Entity)) {
0 ignored issues
show
introduced by
$entity is always a sub-type of AbterPhp\Website\Domain\Entities\Page.
Loading history...
43
            throw new \InvalidArgumentException(__CLASS__ . ':' . __FUNCTION__ . ' expects a Page entity.');
44
        }
45
46
        $query = (new QueryBuilder())
47
            ->update('pages', 'pages', ['deleted' => [1, \PDO::PARAM_INT]])
48
            ->where('id = ?')
49
            ->addUnnamedPlaceholderValue($entity->getId(), \PDO::PARAM_STR);
50
51
        $sql    = $query->getSql();
52
        $params = $query->getParameters();
53
54
        $statement = $this->writeConnection->prepare($sql);
55
        $statement->bindValues($params);
56
        $statement->execute();
57
    }
58
59
    /**
60
     * @return array
61
     */
62
    public function getAll(): array
63
    {
64
        $query = $this->getBaseQuery();
65
66
        $sql = $query->getSql();
67
68
        return $this->read($sql, [], self::VALUE_TYPE_ARRAY);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->read($sql,...self::VALUE_TYPE_ARRAY) could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
69
    }
70
71
    /**
72
     * @param int      $limitFrom
73
     * @param int      $pageSize
74
     * @param string[] $orders
75
     * @param array    $conditions
76
     * @param array    $params
77
     *
78
     * @return Entity[]
79
     * @throws \Opulence\Orm\OrmException
80
     */
81
    public function getPage(int $limitFrom, int $pageSize, array $orders, array $conditions, array $params): array
82
    {
83
        $query = $this->getGridQuery()
84
            ->limit($pageSize)
85
            ->offset($limitFrom);
86
87
        foreach ($orders as $order) {
88
            $query->addOrderBy($order);
89
        }
90
91
        foreach ($conditions as $condition) {
92
            $query->andWhere($condition);
93
        }
94
95
        $replaceCount = 1;
96
97
        $sql = $query->getSql();
98
        $sql = str_replace('SELECT', 'SELECT SQL_CALC_FOUND_ROWS', $sql, $replaceCount);
99
100
        return $this->read($sql, $params, self::VALUE_TYPE_ARRAY);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->read($sql,...self::VALUE_TYPE_ARRAY) could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
101
    }
102
103
    /**
104
     * @param int|string $id
105
     *
106
     * @return Entity|null
107
     */
108
    public function getById($id)
109
    {
110
        $query = $this->getExtendedQuery()->andWhere('pages.id = :page_id');
111
112
        $sql    = $query->getSql();
113
        $params = [
114
            'page_id' => [$id, \PDO::PARAM_STR],
115
        ];
116
117
        return $this->read($sql, $params, self::VALUE_TYPE_ENTITY, true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->read($sql,...ALUE_TYPE_ENTITY, true) also could return the type array which is incompatible with the documented return type AbterPhp\Website\Domain\Entities\Page|null.
Loading history...
118
    }
119
120
    /**
121
     * @param string $title
122
     *
123
     * @return ?Entity
124
     */
125
    public function getByIdentifier(string $identifier): ?Entity
126
    {
127
        $query = $this->getExtendedQuery()->andWhere('pages.identifier = :identifier');
128
129
        $sql    = $query->getSql();
130
        $params = [
131
            'identifier' => [$identifier, \PDO::PARAM_STR],
132
        ];
133
134
        return $this->read($sql, $params, self::VALUE_TYPE_ENTITY, true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->read($sql,...ALUE_TYPE_ENTITY, true) could return the type array which is incompatible with the type-hinted return AbterPhp\Website\Domain\Entities\Page|null. Consider adding an additional type-check to rule them out.
Loading history...
135
    }
136
137
    /**
138
     * @param string $title
139
     *
140
     * @return ?Entity
141
     */
142
    public function getWithLayout(string $identifier): ?Entity
143
    {
144
        $query = $this->getWithLayoutQuery()->andWhere('pages.identifier = :identifier');
145
146
        $sql    = $query->getSql();
147
        $params = [
148
            'identifier' => $identifier,
149
        ];
150
151
        return $this->read($sql, $params, self::VALUE_TYPE_ENTITY, true);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->read($sql,...ALUE_TYPE_ENTITY, true) could return the type array which is incompatible with the type-hinted return AbterPhp\Website\Domain\Entities\Page|null. Consider adding an additional type-check to rule them out.
Loading history...
152
    }
153
154
    /**
155
     * @param Entity $entity
156
     */
157
    public function update($entity)
158
    {
159
        if (!($entity instanceof Entity)) {
0 ignored issues
show
introduced by
$entity is always a sub-type of AbterPhp\Website\Domain\Entities\Page.
Loading history...
160
            throw new \InvalidArgumentException(__CLASS__ . ':' . __FUNCTION__ . ' expects a Page entity.');
161
        }
162
163
        $columnNamesToValues = $this->getColumnNamesToValues($entity, false);
164
165
        $query = (new QueryBuilder())
166
            ->update('pages', 'pages', $columnNamesToValues)
167
            ->where('id = ?')
168
            ->andWhere('deleted = 0')
169
            ->addUnnamedPlaceholderValue($entity->getId(), \PDO::PARAM_STR);
170
171
        $sql    = $query->getSql();
172
        $params = $query->getParameters();
173
174
        $statement = $this->writeConnection->prepare($sql);
175
        $statement->bindValues($params);
176
        $statement->execute();
177
    }
178
179
    /**
180
     * @param Entity $entity
181
     * @param bool   $create
182
     *
183
     * @return array
184
     */
185
    protected function getColumnNamesToValues(Entity $entity, bool $create): array
186
    {
187
        $layoutIdType = \PDO::PARAM_NULL;
188
        if ($entity->getLayoutId()) {
189
            $layoutIdType = \PDO::PARAM_STR;
190
        }
191
192
        $categoryId     = null;
193
        $categoryIdType = \PDO::PARAM_NULL;
194
        if ($entity->getCategory()) {
195
            $categoryIdType = \PDO::PARAM_STR;
196
            $categoryId     = $entity->getCategory()->getId();
197
        }
198
199
        $columnNamesToValues = [
200
            'identifier'  => [$entity->getIdentifier(), \PDO::PARAM_STR],
201
            'title'       => [$entity->getTitle(), \PDO::PARAM_STR],
202
            'body'        => [$entity->getBody(), \PDO::PARAM_STR],
203
            'category_id' => [$categoryId, $categoryIdType],
204
            'layout'      => [$entity->getLayout(), \PDO::PARAM_STR],
205
            'layout_id'   => [$entity->getLayoutId(), $layoutIdType],
206
        ];
207
208
        if ($create) {
209
            $columnNamesToValues = array_merge(['id' => [$entity->getId(), \PDO::PARAM_STR]], $columnNamesToValues);
210
        }
211
212
        $columnNamesToValues = $this->populateWithMeta($entity, $columnNamesToValues);
213
        $columnNamesToValues = $this->populateWithAssets($entity, $columnNamesToValues);
214
215
        return $columnNamesToValues;
216
    }
217
218
    /**
219
     * @param Entity $entity
220
     * @param array  $columnNamesToValues
221
     *
222
     * @return array
223
     */
224
    protected function populateWithMeta(Entity $entity, array $columnNamesToValues): array
225
    {
226
        if (!$entity->getMeta()) {
227
            return $columnNamesToValues;
228
        }
229
230
        $meta = $entity->getMeta();
231
232
        $metaValues = [
233
            'meta_description'    => $meta->getDescription(),
234
            'meta_robots'         => $meta->getRobots(),
235
            'meta_author'         => $meta->getAuthor(),
236
            'meta_copyright'      => $meta->getCopyright(),
237
            'meta_keywords'       => $meta->getKeywords(),
238
            'meta_og_title'       => $meta->getOGTitle(),
239
            'meta_og_image'       => $meta->getOGImage(),
240
            'meta_og_description' => $meta->getOGDescription(),
241
        ];
242
243
        return array_merge($columnNamesToValues, $metaValues);
244
    }
245
246
    /**
247
     * @param Entity $entity
248
     * @param array  $columnNamesToValues
249
     *
250
     * @return array
251
     */
252
    protected function populateWithAssets(Entity $entity, array $columnNamesToValues): array
253
    {
254
        if (!$entity->getAssets()) {
255
            return $columnNamesToValues;
256
        }
257
258
        $assets = $entity->getAssets();
259
260
        $assetValues = [
261
            'header'    => $assets->getHeader(),
262
            'footer'    => $assets->getFooter(),
263
            'css_files' => implode("\n\r", $assets->getCssFiles()),
264
            'js_files'  => implode("\n\r", $assets->getJsFiles()),
265
        ];
266
267
        return array_merge($columnNamesToValues, $assetValues);
268
    }
269
270
    /**
271
     * @param array $hash
272
     *
273
     * @return Entity
274
     */
275
    protected function loadEntity(array $hash)
276
    {
277
        $meta     = $this->loadMeta($hash);
278
        $assets   = $this->loadAssets($hash);
279
        $body     = empty($hash['body']) ? '' : $hash['body'];
280
        $category = $this->loadCategory($hash);
281
        $layout   = empty($hash['layout']) ? '' : $hash['layout'];
282
        $layoutId = empty($hash['layout_id']) ? null : $hash['layout_id'];
283
284
        return new Entity(
285
            $hash['id'],
286
            $hash['identifier'],
287
            $hash['title'],
288
            $body,
289
            $category,
290
            $layout,
291
            $layoutId,
292
            $meta,
293
            $assets
294
        );
295
    }
296
297
    /**
298
     * @param array $hash
299
     *
300
     * @return Entity\Meta|null
301
     */
302
    protected function loadMeta(array $hash): ?Entity\Meta
303
    {
304
        if (!array_key_exists('meta_description', $hash)) {
305
            return null;
306
        }
307
308
        return new Entity\Meta(
309
            $hash['meta_description'],
310
            $hash['meta_robots'],
311
            $hash['meta_author'],
312
            $hash['meta_copyright'],
313
            $hash['meta_keywords'],
314
            $hash['meta_og_title'],
315
            $hash['meta_og_image'],
316
            $hash['meta_og_description']
317
        );
318
    }
319
320
    /**
321
     * @param array $hash
322
     *
323
     * @return PageAssets|null
324
     */
325
    protected function loadAssets(array $hash): ?PageAssets
326
    {
327
        if (!array_key_exists('css_files', $hash)) {
328
            return null;
329
        }
330
331
        $layoutAssets = $this->loadLayoutAssets($hash);
332
333
        return new PageAssets(
334
            $hash['identifier'],
335
            $hash['header'],
336
            $hash['footer'],
337
            $this->extractFiles($hash['css_files']),
338
            $this->extractFiles($hash['js_files']),
339
            $layoutAssets
340
        );
341
    }
342
343
    /**
344
     * @param array $hash
345
     *
346
     * @return PageCategory|null
347
     */
348
    protected function loadCategory(array $hash): ?PageCategory
349
    {
350
        $id         = isset($hash['category_id']) ? $hash['category_id'] : '';
351
        $name       = isset($hash['category_name']) ? $hash['category_name'] : '';
352
        $identifier = isset($hash['category_identifier']) ? $hash['category_identifier'] : '';
353
354
        if (!$id && !$name && !$identifier) {
355
            return null;
356
        }
357
358
        return new PageCategory($id, $name, $identifier);
359
    }
360
361
    /**
362
     * @param array $hash
363
     *
364
     * @return LayoutAssets|null
365
     */
366
    protected function loadLayoutAssets(array $hash): ?LayoutAssets
367
    {
368
        if (!array_key_exists('layout_css_files', $hash) || null === $hash['layout_css_files']) {
369
            return null;
370
        }
371
372
        return new LayoutAssets(
373
            $hash['layout_identifier'],
374
            $hash['layout_header'],
375
            $hash['layout_footer'],
376
            $this->extractFiles($hash['layout_css_files']),
377
            $this->extractFiles($hash['layout_js_files'])
378
        );
379
    }
380
381
    /**
382
     * @param string $rawData
383
     *
384
     * @return string[]
385
     */
386
    private function extractFiles(string $rawData): array
387
    {
388
        if (empty($rawData)) {
389
            return [];
390
        }
391
392
        return explode("\n", str_replace("\r", "", trim($rawData)));
393
    }
394
395
    /**
396
     * @return SelectQuery
397
     */
398
    private function getBaseQuery(): SelectQuery
399
    {
400
        /** @var SelectQuery $query */
401
        $query = (new QueryBuilder())
402
            ->select(
403
                'pages.id',
404
                'pages.identifier',
405
                'pages.title',
406
                'pages.category_id',
407
                'pages.layout_id'
408
            )
409
            ->from('pages')
410
            ->where('pages.deleted = 0');
411
412
        return $query;
413
    }
414
415
    /**
416
     * @return SelectQuery
417
     */
418
    private function getGridQuery(): SelectQuery
419
    {
420
        /** @var SelectQuery $query */
421
        $query = (new QueryBuilder())
422
            ->select(
423
                'pages.id',
424
                'pages.identifier',
425
                'pages.title',
426
                'categories.name AS category_name'
427
            )
428
            ->from('pages')
429
            ->leftJoin('page_categories', 'categories', 'categories.id = pages.category_id')
430
            ->where('pages.deleted = 0');
431
432
        return $query;
433
    }
434
435
    /**
436
     * @return SelectQuery
437
     */
438
    private function getExtendedQuery(): SelectQuery
439
    {
440
        /** @var SelectQuery $query */
441
        $query = (new QueryBuilder())
442
            ->select(
443
                'pages.id',
444
                'pages.identifier',
445
                'pages.title',
446
                'pages.body',
447
                'pages.category_id',
448
                'pages.layout_id',
449
                'pages.layout',
450
                'pages.meta_description',
451
                'pages.meta_robots',
452
                'pages.meta_author',
453
                'pages.meta_copyright',
454
                'pages.meta_keywords',
455
                'pages.meta_og_title',
456
                'pages.meta_og_image',
457
                'pages.meta_og_description',
458
                'pages.header',
459
                'pages.footer',
460
                'pages.css_files',
461
                'pages.js_files'
462
            )
463
            ->from('pages')
464
            ->where('pages.deleted = 0');
465
466
        return $query;
467
    }
468
469
    /**
470
     * @return SelectQuery
471
     */
472
    private function getWithLayoutQuery(): SelectQuery
473
    {
474
        /** @var SelectQuery $query */
475
        $query = (new QueryBuilder())
476
            ->select(
477
                'pages.id',
478
                'pages.identifier',
479
                'pages.title',
480
                'pages.body',
481
                'pages.category_id',
482
                'pages.layout_id',
483
                'COALESCE(layouts.body, pages.layout) AS layout',
484
                'pages.meta_description',
485
                'pages.meta_robots',
486
                'pages.meta_author',
487
                'pages.meta_copyright',
488
                'pages.meta_keywords',
489
                'pages.meta_og_title',
490
                'pages.meta_og_image',
491
                'pages.meta_og_description',
492
                'pages.header AS header',
493
                'pages.footer AS footer',
494
                'pages.css_files AS css_files',
495
                'pages.js_files AS js_files',
496
                'layouts.identifier AS layout_identifier',
497
                'layouts.header AS layout_header',
498
                'layouts.footer AS layout_footer',
499
                'layouts.css_files AS layout_css_files',
500
                'layouts.js_files AS layout_js_files'
501
            )
502
            ->from('pages')
503
            ->leftJoin('page_layouts', 'layouts', 'layouts.id = pages.layout_id')
504
            ->where('pages.deleted = 0')
505
            ->andWhere('layouts.deleted = 0 OR layouts.deleted IS NULL');
506
507
        return $query;
508
    }
509
}
510