Passed
Push — master ( 4a146d...3e1455 )
by Peter
02:17
created

PageSqlDataMapper::getByCategoryIdentifiers()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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