Completed
Push — EZP-26146-location-swap-urlali... ( 3d141e...44a3c1 )
by
unknown
31:05
created

DoctrineDatabase::cleanupAfterSwap()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 51
Code Lines 33

Duplication

Lines 6
Ratio 11.76 %

Importance

Changes 0
Metric Value
cc 3
eloc 33
nc 3
nop 4
dl 6
loc 51
rs 9.4109
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * File containing the DoctrineDatabase UrlAlias Gateway class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 *
9
 * @version //autogentag//
10
 */
11
namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway;
12
13
use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway;
14
use eZ\Publish\Core\Persistence\Database\DatabaseHandler;
15
use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator;
16
use eZ\Publish\Core\Persistence\Database\Query;
17
use PDO;
18
19
/**
20
 * UrlAlias Gateway.
21
 */
22
class DoctrineDatabase extends Gateway
23
{
24
    /**
25
     * 2^30, since PHP_INT_MAX can cause overflows in DB systems, if PHP is run
26
     * on 64 bit systems.
27
     */
28
    const MAX_LIMIT = 1073741824;
29
30
    /**
31
     * Columns of database tables.
32
     *
33
     * @var array
34
     *
35
     * @todo remove after testing
36
     */
37
    protected $columns = array(
38
        'ezurlalias_ml' => array(
39
            'action',
40
            'action_type',
41
            'alias_redirects',
42
            'id',
43
            'is_alias',
44
            'is_original',
45
            'lang_mask',
46
            'link',
47
            'parent',
48
            'text',
49
            'text_md5',
50
        ),
51
    );
52
53
    /**
54
     * Doctrine database handler.
55
     *
56
     * @var \eZ\Publish\Core\Persistence\Database\DatabaseHandler
57
     */
58
    protected $dbHandler;
59
60
    /**
61
     * Language mask generator.
62
     *
63
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator
64
     */
65
    protected $languageMaskGenerator;
66
67
    /**
68
     * Creates a new DoctrineDatabase UrlAlias Gateway.
69
     *
70
     * @param \eZ\Publish\Core\Persistence\Database\DatabaseHandler $dbHandler
71
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator $languageMaskGenerator
72
     */
73
    public function __construct(
74
        DatabaseHandler $dbHandler,
75
        LanguageMaskGenerator $languageMaskGenerator
76
    ) {
77
        $this->dbHandler = $dbHandler;
78
        $this->languageMaskGenerator = $languageMaskGenerator;
79
    }
80
81
    /**
82
     * Loads list of aliases by given $locationId.
83
     *
84
     * @param mixed $locationId
85
     * @param bool $custom
86
     * @param mixed $languageId
87
     *
88
     * @return array
89
     */
90
    public function loadLocationEntries($locationId, $custom = false, $languageId = false)
91
    {
92
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
93
        $query = $this->dbHandler->createSelectQuery();
94
        $query->select(
95
            $this->dbHandler->quoteColumn('id'),
96
            $this->dbHandler->quoteColumn('link'),
97
            $this->dbHandler->quoteColumn('is_alias'),
98
            $this->dbHandler->quoteColumn('alias_redirects'),
99
            $this->dbHandler->quoteColumn('lang_mask'),
100
            $this->dbHandler->quoteColumn('is_original'),
101
            $this->dbHandler->quoteColumn('parent'),
102
            $this->dbHandler->quoteColumn('text'),
103
            $this->dbHandler->quoteColumn('text_md5'),
104
            $this->dbHandler->quoteColumn('action')
105
        )->from(
106
            $this->dbHandler->quoteTable('ezurlalias_ml')
107
        )->where(
108
            $query->expr->lAnd(
109
                $query->expr->eq(
110
                    $this->dbHandler->quoteColumn('action'),
111
                    $query->bindValue("eznode:{$locationId}", null, \PDO::PARAM_STR)
112
                ),
113
                $query->expr->eq(
114
                    $this->dbHandler->quoteColumn('is_original'),
115
                    $query->bindValue(1, null, \PDO::PARAM_INT)
116
                ),
117
                $query->expr->eq(
118
                    $this->dbHandler->quoteColumn('is_alias'),
119
                    $query->bindValue(
120
                        $custom ? 1 : 0,
121
                        null,
122
                        \PDO::PARAM_INT
123
                    )
124
                )
125
            )
126
        );
127
128 View Code Duplication
        if ($languageId !== false) {
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...
129
            $query->where(
130
                $query->expr->gt(
131
                    $query->expr->bitAnd(
132
                        $this->dbHandler->quoteColumn('lang_mask'),
133
                        $query->bindValue($languageId, null, \PDO::PARAM_INT)
134
                    ),
135
                    0
136
                )
137
            );
138
        }
139
140
        $statement = $query->prepare();
141
        $statement->execute();
142
143
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
144
    }
145
146
    /**
147
     * Loads paged list of global aliases.
148
     *
149
     * @param string|null $languageCode
150
     * @param int $offset
151
     * @param int $limit
152
     *
153
     * @return array
154
     */
155
    public function listGlobalEntries($languageCode = null, $offset = 0, $limit = -1)
156
    {
157
        $limit = $limit === -1 ? self::MAX_LIMIT : $limit;
158
159
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
160
        $query = $this->dbHandler->createSelectQuery();
161
        $query->select(
162
            $this->dbHandler->quoteColumn('action'),
163
            $this->dbHandler->quoteColumn('id'),
164
            $this->dbHandler->quoteColumn('link'),
165
            $this->dbHandler->quoteColumn('is_alias'),
166
            $this->dbHandler->quoteColumn('alias_redirects'),
167
            $this->dbHandler->quoteColumn('lang_mask'),
168
            $this->dbHandler->quoteColumn('is_original'),
169
            $this->dbHandler->quoteColumn('parent'),
170
            $this->dbHandler->quoteColumn('text_md5')
171
        )->from(
172
            $this->dbHandler->quoteTable('ezurlalias_ml')
173
        )->where(
174
            $query->expr->lAnd(
175
                $query->expr->eq(
176
                    $this->dbHandler->quoteColumn('action_type'),
177
                    $query->bindValue('module', null, \PDO::PARAM_STR)
178
                ),
179
                $query->expr->eq(
180
                    $this->dbHandler->quoteColumn('is_original'),
181
                    $query->bindValue(1, null, \PDO::PARAM_INT)
182
                ),
183
                $query->expr->eq(
184
                    $this->dbHandler->quoteColumn('is_alias'),
185
                    $query->bindValue(1, null, \PDO::PARAM_INT)
186
                )
187
            )
188
        )->limit(
189
            $limit,
190
            $offset
191
        );
192 View Code Duplication
        if (isset($languageCode)) {
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...
193
            $query->where(
194
                $query->expr->gt(
195
                    $query->expr->bitAnd(
196
                        $this->dbHandler->quoteColumn('lang_mask'),
197
                        $query->bindValue(
198
                            $this->languageMaskGenerator->generateLanguageIndicator($languageCode, false),
199
                            null,
200
                            \PDO::PARAM_INT
201
                        )
202
                    ),
203
                    0
204
                )
205
            );
206
        }
207
        $statement = $query->prepare();
208
        $statement->execute();
209
210
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
211
    }
212
213
    /**
214
     * Returns boolean indicating if the row with given $id is special root entry.
215
     *
216
     * Special root entry entry will have parentId=0 and text=''.
217
     * In standard installation this entry will point to location with id=2.
218
     *
219
     * @param mixed $id
220
     *
221
     * @return bool
222
     */
223
    public function isRootEntry($id)
224
    {
225
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
226
        $query = $this->dbHandler->createSelectQuery();
227
        $query->select(
228
            $this->dbHandler->quoteColumn('text'),
229
            $this->dbHandler->quoteColumn('parent')
230
        )->from(
231
            $this->dbHandler->quoteTable('ezurlalias_ml')
232
        )->where(
233
            $query->expr->eq(
234
                $this->dbHandler->quoteColumn('id'),
235
                $query->bindValue($id, null, \PDO::PARAM_INT)
236
            )
237
        );
238
        $statement = $query->prepare();
239
        $statement->execute();
240
        $row = $statement->fetch(\PDO::FETCH_ASSOC);
241
242
        return strlen($row['text']) == 0 && $row['parent'] == 0;
243
    }
244
245
    /**
246
     * Downgrades autogenerated entry matched by given $action and $languageId and negatively matched by
247
     * composite primary key.
248
     *
249
     * If language mask of the found entry is composite (meaning it consists of multiple language ids) given
250
     * $languageId will be removed from mask. Otherwise entry will be marked as history.
251
     *
252
     * @param string $action
253
     * @param mixed $languageId
254
     * @param mixed $newId
255
     * @param mixed $parentId
256
     * @param string $textMD5
257
     */
258
    public function cleanupAfterPublish($action, $languageId, $newId, $parentId, $textMD5)
259
    {
260
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
261
        $query = $this->dbHandler->createSelectQuery();
262
        $query->select(
263
            $this->dbHandler->quoteColumn('parent'),
264
            $this->dbHandler->quoteColumn('text_md5'),
265
            $this->dbHandler->quoteColumn('lang_mask')
266
        )->from(
267
            $this->dbHandler->quoteTable('ezurlalias_ml')
268
        )->where(
269
            $query->expr->lAnd(
270
                // 1) Autogenerated aliases that match action and language...
271
                $query->expr->eq(
272
                    $this->dbHandler->quoteColumn('action'),
273
                    $query->bindValue($action, null, \PDO::PARAM_STR)
274
                ),
275
                $query->expr->eq(
276
                    $this->dbHandler->quoteColumn('is_original'),
277
                    $query->bindValue(1, null, \PDO::PARAM_INT)
278
                ),
279
                $query->expr->eq(
280
                    $this->dbHandler->quoteColumn('is_alias'),
281
                    $query->bindValue(0, null, \PDO::PARAM_INT)
282
                ),
283
                $query->expr->gt(
284
                    $query->expr->bitAnd(
285
                        $this->dbHandler->quoteColumn('lang_mask'),
286
                        $query->bindValue($languageId, null, \PDO::PARAM_INT)
287
                    ),
288
                    0
289
                ),
290
                // 2) ...but not newly published entry
291
                $query->expr->not(
292
                    $query->expr->lAnd(
293
                        $query->expr->eq(
294
                            $this->dbHandler->quoteColumn('parent'),
295
                            $query->bindValue($parentId, null, \PDO::PARAM_INT)
296
                        ),
297
                        $query->expr->eq(
298
                            $this->dbHandler->quoteColumn('text_md5'),
299
                            $query->bindValue($textMD5, null, \PDO::PARAM_STR)
300
                        )
301
                    )
302
                )
303
            )
304
        );
305
306
        $statement = $query->prepare();
307
        $statement->execute();
308
        $row = $statement->fetch(\PDO::FETCH_ASSOC);
309
310 View Code Duplication
        if (!empty($row)) {
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...
311
            // If language mask is composite (consists of multiple languages) then remove given language from entry
312
            if ($row['lang_mask'] & ~($languageId | 1)) {
313
                $this->removeTranslation($row['parent'], $row['text_md5'], $languageId);
314
            } else {
315
                // Otherwise mark entry as history
316
                $this->historize($row['parent'], $row['text_md5'], $newId);
317
            }
318
        }
319
    }
320
321
    public function cleanupAfterSwap($action, $languageId, $newId, $languageMask)
322
    {
323
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
324
        $query = $this->dbHandler->createSelectQuery();
325
        $query->select(
326
            $this->dbHandler->quoteColumn('parent'),
327
            $this->dbHandler->quoteColumn('text'),
328
            $this->dbHandler->quoteColumn('text_md5'),
329
            $this->dbHandler->quoteColumn('lang_mask')
330
        )->from(
331
            $this->dbHandler->quoteTable('ezurlalias_ml')
332
        )->where(
333
            $query->expr->lAnd(
334
                // 1) Autogenerated aliases that match action and language...
335
                $query->expr->eq(
336
                    $this->dbHandler->quoteColumn('action'),
337
                    $query->bindValue($action, null, \PDO::PARAM_STR)
338
                ),
339
                $query->expr->eq(
340
                    $this->dbHandler->quoteColumn('is_original'),
341
                    $query->bindValue(1, null, \PDO::PARAM_INT)
342
                ),
343
                $query->expr->eq(
344
                    $this->dbHandler->quoteColumn('is_alias'),
345
                    $query->bindValue(0, null, \PDO::PARAM_INT)
346
                ),
347
                $query->expr->gt(
348
                    $query->expr->bitAnd(
349
                        $this->dbHandler->quoteColumn('lang_mask'),
350
                        $query->bindValue($languageId, null, \PDO::PARAM_INT)
351
                    ),
352
                    0
353
                )
354
            )
355
        );
356
357
        $statement = $query->prepare();
358
        $statement->execute();
359
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);
360
361
        foreach ($rows as $row) {
362
            // If entry was reused for publishing new swapped aliases, it is composite and we need
363
            // to keep it, so just remove translation
364 View Code Duplication
            if ($row['lang_mask'] & $languageMask) {
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...
365
                $this->removeTranslation($row['parent'], $row['text_md5'], $languageId);
366
            } else {
367
                // Otherwise mark the whole entry as history
368
                $this->historize($row['parent'], $row['text_md5'], $newId);
369
            }
370
        }
371
    }
372
373
    /**
374
     * Updates single row matched by composite primary key.
375
     *
376
     * Sets "is_original" to 0 thus marking entry as history.
377
     *
378
     * Re-links history entries.
379
     *
380
     * When location alias is published we need to check for new history entries created with self::downgrade()
381
     * with the same action and language, update their "link" column with id of the published entry.
382
     * History entry "id" column is moved to next id value so that all active (non-history) entries are kept
383
     * under the same id.
384
     *
385
     * @param mixed $parentId
386
     * @param string $textMD5
387
     */
388 View Code Duplication
    protected function historize($parentId, $textMD5, $newId)
389
    {
390
        /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
391
        $query = $this->dbHandler->createUpdateQuery();
392
        $query->update(
393
            $this->dbHandler->quoteTable('ezurlalias_ml')
394
        )->set(
395
            $this->dbHandler->quoteColumn('is_original'),
396
            $query->bindValue(0, null, \PDO::PARAM_INT)
397
        )->set(
398
            $this->dbHandler->quoteColumn('link'),
399
            $query->bindValue($newId, null, \PDO::PARAM_INT)
400
        )->set(
401
            $this->dbHandler->quoteColumn('id'),
402
            $query->bindValue(
403
                $this->getNextId(),
404
                null,
405
                \PDO::PARAM_INT
406
            )
407
        )->where(
408
            $query->expr->lAnd(
409
                $query->expr->eq(
410
                    $this->dbHandler->quoteColumn('parent'),
411
                    $query->bindValue($parentId, null, \PDO::PARAM_INT)
412
                ),
413
                $query->expr->eq(
414
                    $this->dbHandler->quoteColumn('text_md5'),
415
                    $query->bindValue($textMD5, null, \PDO::PARAM_STR)
416
                )
417
            )
418
        );
419
        $query->prepare()->execute();
420
    }
421
422
    /**
423
     * Updates single row data matched by composite primary key.
424
     *
425
     * Removes given $languageId from entry's language mask
426
     *
427
     * @param mixed $parentId
428
     * @param string $textMD5
429
     * @param mixed $languageId
430
     */
431 View Code Duplication
    protected function removeTranslation($parentId, $textMD5, $languageId)
432
    {
433
        /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
434
        $query = $this->dbHandler->createUpdateQuery();
435
        $query->update(
436
            $this->dbHandler->quoteTable('ezurlalias_ml')
437
        )->set(
438
            $this->dbHandler->quoteColumn('lang_mask'),
439
            $query->expr->bitAnd(
440
                $this->dbHandler->quoteColumn('lang_mask'),
441
                $query->bindValue(~$languageId, null, \PDO::PARAM_INT)
442
            )
443
        )->where(
444
            $query->expr->lAnd(
445
                $query->expr->eq(
446
                    $this->dbHandler->quoteColumn('parent'),
447
                    $query->bindValue($parentId, null, \PDO::PARAM_INT)
448
                ),
449
                $query->expr->eq(
450
                    $this->dbHandler->quoteColumn('text_md5'),
451
                    $query->bindValue($textMD5, null, \PDO::PARAM_STR)
452
                )
453
            )
454
        );
455
        $query->prepare()->execute();
456
    }
457
458
    /**
459
     * Marks all entries with given $id as history entries.
460
     *
461
     * This method is used by Handler::locationMoved(). For this reason rows are not updated with next id value as
462
     * all entries with given id are being marked as history and there is no need for id separation.
463
     * Thus only "link" and "is_original" columns are updated.
464
     *
465
     * @param mixed $id
466
     * @param mixed $link
467
     */
468 View Code Duplication
    public function historizeId($id, $link)
469
    {
470
        /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
471
        $query = $this->dbHandler->createUpdateQuery();
472
        $query->update(
473
            $this->dbHandler->quoteTable('ezurlalias_ml')
474
        )->set(
475
            $this->dbHandler->quoteColumn('is_original'),
476
            $query->bindValue(0, null, \PDO::PARAM_INT)
477
        )->set(
478
            $this->dbHandler->quoteColumn('link'),
479
            $query->bindValue($link, null, \PDO::PARAM_INT)
480
        )->where(
481
            $query->expr->lAnd(
482
                $query->expr->eq(
483
                    $this->dbHandler->quoteColumn('is_alias'),
484
                    $query->bindValue(0, null, \PDO::PARAM_INT)
485
                ),
486
                $query->expr->eq(
487
                    $this->dbHandler->quoteColumn('action_type'),
488
                    $query->bindValue('eznode', null, \PDO::PARAM_STR)
489
                ),
490
                $query->expr->eq(
491
                    $this->dbHandler->quoteColumn('link'),
492
                    $query->bindValue($id, null, \PDO::PARAM_INT)
493
                )
494
            )
495
        );
496
        $query->prepare()->execute();
497
    }
498
499
    /**
500
     * Updates parent id of autogenerated entries.
501
     *
502
     * Update includes history entries.
503
     *
504
     * @param mixed $oldParentId
505
     * @param mixed $newParentId
506
     */
507 View Code Duplication
    public function reparent($oldParentId, $newParentId)
508
    {
509
        /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
510
        $query = $this->dbHandler->createUpdateQuery();
511
        $query->update(
512
            $this->dbHandler->quoteTable('ezurlalias_ml')
513
        )->set(
514
            $this->dbHandler->quoteColumn('parent'),
515
            $query->bindValue($newParentId, null, \PDO::PARAM_INT)
516
        )->where(
517
            $query->expr->lAnd(
518
                $query->expr->eq(
519
                    $this->dbHandler->quoteColumn('is_alias'),
520
                    $query->bindValue(0, null, \PDO::PARAM_INT)
521
                ),
522
                $query->expr->eq(
523
                    $this->dbHandler->quoteColumn('parent'),
524
                    $query->bindValue($oldParentId, null, \PDO::PARAM_INT)
525
                )
526
            )
527
        );
528
529
        $query->prepare()->execute();
530
    }
531
532
    /**
533
     * Updates single row data matched by composite primary key.
534
     *
535
     * Use optional parameter $languageMaskMatch to additionally limit the query match with languages.
536
     *
537
     * @param mixed $parentId
538
     * @param string $textMD5
539
     * @param array $values associative array with column names as keys and column values as values
540
     */
541 View Code Duplication
    public function updateRow($parentId, $textMD5, array $values)
542
    {
543
        /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
544
        $query = $this->dbHandler->createUpdateQuery();
545
        $query->update($this->dbHandler->quoteTable('ezurlalias_ml'));
546
        $this->setQueryValues($query, $values);
547
        $query->where(
548
            $query->expr->lAnd(
549
                $query->expr->eq(
550
                    $this->dbHandler->quoteColumn('parent'),
551
                    $query->bindValue($parentId, null, \PDO::PARAM_INT)
552
                ),
553
                $query->expr->eq(
554
                    $this->dbHandler->quoteColumn('text_md5'),
555
                    $query->bindValue($textMD5, null, \PDO::PARAM_STR)
556
                )
557
            )
558
        );
559
        $query->prepare()->execute();
560
    }
561
562
    /**
563
     * Inserts new row in urlalias_ml table.
564
     *
565
     * @param array $values
566
     *
567
     * @return mixed
568
     */
569
    public function insertRow(array $values)
570
    {
571
        // @todo remove after testing
572
        if (
573
            !isset($values['text']) ||
574
            !isset($values['text_md5']) ||
575
            !isset($values['action']) ||
576
            !isset($values['parent']) ||
577
            !isset($values['lang_mask'])) {
578
            throw new \Exception('value set is incomplete: ' . var_export($values, true) . ", can't execute insert");
579
        }
580
        if (!isset($values['id'])) {
581
            $values['id'] = $this->getNextId();
582
        }
583
        if (!isset($values['link'])) {
584
            $values['link'] = $values['id'];
585
        }
586
        if (!isset($values['is_original'])) {
587
            $values['is_original'] = ($values['id'] == $values['link'] ? 1 : 0);
588
        }
589
        if (!isset($values['is_alias'])) {
590
            $values['is_alias'] = 0;
591
        }
592
        if (!isset($values['alias_redirects'])) {
593
            $values['alias_redirects'] = 0;
594
        }
595
        if (!isset($values['action_type'])) {
596
            if (preg_match('#^(.+):.*#', $values['action'], $matches)) {
597
                $values['action_type'] = $matches[1];
598
            }
599
        }
600
        if ($values['is_alias']) {
601
            $values['is_original'] = 1;
602
        }
603
        if ($values['action'] === 'nop:') {
604
            $values['is_original'] = 0;
605
        }
606
607
        /** @var $query \eZ\Publish\Core\Persistence\Database\InsertQuery */
608
        $query = $this->dbHandler->createInsertQuery();
609
        $query->insertInto($this->dbHandler->quoteTable('ezurlalias_ml'));
610
        $this->setQueryValues($query, $values);
611
        $query->prepare()->execute();
612
613
        return $values['id'];
614
    }
615
616
    /**
617
     * Sets value for insert or update query.
618
     *
619
     * @param \eZ\Publish\Core\Persistence\Database\Query|\eZ\Publish\Core\Persistence\Database\InsertQuery|\eZ\Publish\Core\Persistence\Database\UpdateQuery $query
620
     * @param array $values
621
     *
622
     * @throws \Exception
623
     */
624
    protected function setQueryValues(Query $query, $values)
625
    {
626
        foreach ($values as $column => $value) {
627
            // @todo remove after testing
628
            if (!in_array($column, $this->columns['ezurlalias_ml'])) {
629
                throw new \Exception("unknown column '$column' for table 'ezurlalias_ml'");
630
            }
631
            switch ($column) {
632
                case 'text':
633
                case 'action':
634
                case 'text_md5':
635
                case 'action_type':
636
                    $pdoDataType = \PDO::PARAM_STR;
637
                    break;
638
                default:
639
                    $pdoDataType = \PDO::PARAM_INT;
640
            }
641
            $query->set(
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface eZ\Publish\Core\Persistence\Database\Query as the method set() does only exist in the following implementations of said interface: eZ\Publish\Core\Persiste...ine\InsertDoctrineQuery, eZ\Publish\Core\Persiste...ine\UpdateDoctrineQuery.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
642
                $this->dbHandler->quoteColumn($column),
643
                $query->bindValue($value, null, $pdoDataType)
644
            );
645
        }
646
    }
647
648
    /**
649
     * Returns next value for "id" column.
650
     *
651
     * @return mixed
652
     */
653 View Code Duplication
    public function getNextId()
654
    {
655
        $sequence = $this->dbHandler->getSequenceName('ezurlalias_ml_incr', 'id');
656
        /** @var $query \eZ\Publish\Core\Persistence\Database\InsertQuery */
657
        $query = $this->dbHandler->createInsertQuery();
658
        $query->insertInto(
659
            $this->dbHandler->quoteTable('ezurlalias_ml_incr')
660
        );
661
        // ezcDatabase does not abstract the "auto increment id"
662
        // INSERT INTO ezurlalias_ml_incr VALUES(DEFAULT) is not an option due
663
        // to this mysql bug: http://bugs.mysql.com/bug.php?id=42270
664
        // as a result we are forced to check which database is currently used
665
        // to generate the correct SQL query
666
        // see https://jira.ez.no/browse/EZP-20652
667
        if ($this->dbHandler->useSequences()) {
668
            $query->set(
669
                $this->dbHandler->quoteColumn('id'),
670
                "nextval('{$sequence}')"
671
            );
672
        } else {
673
            $query->set(
674
                $this->dbHandler->quoteColumn('id'),
675
                $query->bindValue(null, null, \PDO::PARAM_NULL)
676
            );
677
        }
678
        $query->prepare()->execute();
679
680
        return $this->dbHandler->lastInsertId($sequence);
681
    }
682
683
    /**
684
     * Loads single row data matched by composite primary key.
685
     *
686
     * @param mixed $parentId
687
     * @param string $textMD5
688
     *
689
     * @return array
690
     */
691 View Code Duplication
    public function loadRow($parentId, $textMD5)
692
    {
693
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
694
        $query = $this->dbHandler->createSelectQuery();
695
        $query->select('*')->from(
696
            $this->dbHandler->quoteTable('ezurlalias_ml')
697
        )->where(
698
            $query->expr->lAnd(
699
                $query->expr->eq(
700
                    $this->dbHandler->quoteColumn('parent'),
701
                    $query->bindValue($parentId, null, \PDO::PARAM_INT)
702
                ),
703
                $query->expr->eq(
704
                    $this->dbHandler->quoteColumn('text_md5'),
705
                    $query->bindValue($textMD5, null, \PDO::PARAM_STR)
706
                )
707
            )
708
        );
709
710
        $statement = $query->prepare();
711
        $statement->execute();
712
713
        return $statement->fetch(\PDO::FETCH_ASSOC);
714
    }
715
716
    /**
717
     * Loads complete URL alias data by given array of path hashes.
718
     *
719
     * @param string[] $urlHashes URL string hashes
720
     *
721
     * @return array
722
     */
723
    public function loadUrlAliasData(array $urlHashes)
724
    {
725
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
726
        $query = $this->dbHandler->createSelectQuery();
727
728
        $count = count($urlHashes);
729
        foreach ($urlHashes as $level => $urlPartHash) {
730
            $tableName = 'ezurlalias_ml' . ($level === $count - 1 ? '' : $level);
731
732
            if ($level === $count - 1) {
733
                $query->select(
734
                    $this->dbHandler->quoteColumn('id', $tableName),
735
                    $this->dbHandler->quoteColumn('link', $tableName),
736
                    $this->dbHandler->quoteColumn('is_alias', $tableName),
737
                    $this->dbHandler->quoteColumn('alias_redirects', $tableName),
738
                    $this->dbHandler->quoteColumn('is_original', $tableName),
739
                    $this->dbHandler->quoteColumn('action', $tableName),
740
                    $this->dbHandler->quoteColumn('action_type', $tableName),
741
                    $this->dbHandler->quoteColumn('lang_mask', $tableName),
742
                    $this->dbHandler->quoteColumn('text', $tableName),
743
                    $this->dbHandler->quoteColumn('parent', $tableName),
744
                    $this->dbHandler->quoteColumn('text_md5', $tableName)
745
                )->from(
746
                    $this->dbHandler->quoteTable('ezurlalias_ml')
747
                );
748
            } else {
749
                $query->select(
750
                    $this->dbHandler->aliasedColumn($query, 'id', $tableName),
751
                    $this->dbHandler->aliasedColumn($query, 'link', $tableName),
752
                    $this->dbHandler->aliasedColumn($query, 'is_alias', $tableName),
753
                    $this->dbHandler->aliasedColumn($query, 'alias_redirects', $tableName),
754
                    $this->dbHandler->aliasedColumn($query, 'is_original', $tableName),
755
                    $this->dbHandler->aliasedColumn($query, 'action', $tableName),
756
                    $this->dbHandler->aliasedColumn($query, 'action_type', $tableName),
757
                    $this->dbHandler->aliasedColumn($query, 'lang_mask', $tableName),
758
                    $this->dbHandler->aliasedColumn($query, 'text', $tableName),
759
                    $this->dbHandler->aliasedColumn($query, 'parent', $tableName),
760
                    $this->dbHandler->aliasedColumn($query, 'text_md5', $tableName)
761
                )->from(
762
                    $query->alias('ezurlalias_ml', $tableName)
763
                );
764
            }
765
766
            $query->where(
767
                $query->expr->lAnd(
768
                    $query->expr->eq(
769
                        $this->dbHandler->quoteColumn('text_md5', $tableName),
770
                        $query->bindValue($urlPartHash, null, \PDO::PARAM_STR)
771
                    ),
772
                    $query->expr->eq(
773
                        $this->dbHandler->quoteColumn('parent', $tableName),
774
                        // root entry has parent column set to 0
775
                        isset($previousTableName) ? $this->dbHandler->quoteColumn('link', $previousTableName) : $query->bindValue(0, null, \PDO::PARAM_INT)
776
                    )
777
                )
778
            );
779
780
            $previousTableName = $tableName;
781
        }
782
        $query->limit(1);
783
784
        $statement = $query->prepare();
785
        $statement->execute();
786
787
        return $statement->fetch(\PDO::FETCH_ASSOC);
788
    }
789
790
    /**
791
     * Loads autogenerated entry id by given $action and optionally $parentId.
792
     *
793
     * @param string $action
794
     * @param mixed|null $parentId
795
     *
796
     * @return array
797
     */
798 View Code Duplication
    public function loadAutogeneratedEntry($action, $parentId = null)
799
    {
800
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
801
        $query = $this->dbHandler->createSelectQuery();
802
        $query->select(
803
            '*'
804
        )->from(
805
            $this->dbHandler->quoteTable('ezurlalias_ml')
806
        )->where(
807
            $query->expr->lAnd(
808
                $query->expr->eq(
809
                    $this->dbHandler->quoteColumn('action'),
810
                    $query->bindValue($action, null, \PDO::PARAM_STR)
811
                ),
812
                $query->expr->eq(
813
                    $this->dbHandler->quoteColumn('is_original'),
814
                    $query->bindValue(1, null, \PDO::PARAM_INT)
815
                ),
816
                $query->expr->eq(
817
                    $this->dbHandler->quoteColumn('is_alias'),
818
                    $query->bindValue(0, null, \PDO::PARAM_INT)
819
                )
820
            )
821
        );
822
823
        if (isset($parentId)) {
824
            $query->where(
825
                $query->expr->eq(
826
                    $this->dbHandler->quoteColumn('parent'),
827
                    $query->bindValue($parentId, null, \PDO::PARAM_INT)
828
                )
829
            );
830
        }
831
832
        $statement = $query->prepare();
833
        $statement->execute();
834
835
        return $statement->fetch(\PDO::FETCH_ASSOC);
836
    }
837
838
    /**
839
     * Loads all data for the path identified by given $id.
840
     *
841
     * @throws \RuntimeException
842
     *
843
     * @param mixed $id
844
     *
845
     * @return array
846
     */
847
    public function loadPathData($id)
848
    {
849
        $pathData = array();
850
851
        while ($id != 0) {
852
            /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
853
            $query = $this->dbHandler->createSelectQuery();
854
            $query->select(
855
                $this->dbHandler->quoteColumn('parent'),
856
                $this->dbHandler->quoteColumn('lang_mask'),
857
                $this->dbHandler->quoteColumn('text')
858
            )->from(
859
                $this->dbHandler->quoteTable('ezurlalias_ml')
860
            )->where(
861
                $query->expr->eq(
862
                    $this->dbHandler->quoteColumn('id'),
863
                    $query->bindValue($id, null, \PDO::PARAM_INT)
864
                )
865
            );
866
867
            $statement = $query->prepare();
868
            $statement->execute();
869
870
            $rows = $statement->fetchAll(\PDO::FETCH_ASSOC);
871
            if (empty($rows)) {
872
                // Normally this should never happen
873
                // @todo remove throw when tested
874
                $path = implode('/', $pathData);
875
                throw new \RuntimeException("Path ({$path}...) is broken, last id is '{$id}': " . __METHOD__);
876
            }
877
878
            $id = $rows[0]['parent'];
879
            array_unshift($pathData, $rows);
880
        }
881
882
        return $pathData;
883
    }
884
885
    /**
886
     * Loads path data identified by given ordered array of hierarchy data.
887
     *
888
     * The first entry in $hierarchyData corresponds to the top-most path element in the path, the second entry the
889
     * child of the first path element and so on.
890
     * This method is faster than self::getPath() since it can fetch all elements using only one query, but can be used
891
     * only for autogenerated paths.
892
     *
893
     * @param array $hierarchyData
894
     *
895
     * @return array
896
     */
897
    public function loadPathDataByHierarchy(array $hierarchyData)
898
    {
899
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
900
        $query = $this->dbHandler->createSelectQuery();
901
902
        $hierarchyConditions = array();
903
        foreach ($hierarchyData as $levelData) {
904
            $hierarchyConditions[] = $query->expr->lAnd(
905
                $query->expr->eq(
906
                    $this->dbHandler->quoteColumn('parent'),
907
                    $query->bindValue(
908
                        $levelData['parent'],
909
                        null,
910
                        \PDO::PARAM_INT
911
                    )
912
                ),
913
                $query->expr->eq(
914
                    $this->dbHandler->quoteColumn('action'),
915
                    $query->bindValue(
916
                        $levelData['action'],
917
                        null,
918
                        \PDO::PARAM_STR
919
                    )
920
                ),
921
                $query->expr->eq(
922
                    $this->dbHandler->quoteColumn('id'),
923
                    $query->bindValue(
924
                        $levelData['id'],
925
                        null,
926
                        \PDO::PARAM_INT
927
                    )
928
                )
929
            );
930
        }
931
932
        $query->select(
933
            $this->dbHandler->quoteColumn('action'),
934
            $this->dbHandler->quoteColumn('lang_mask'),
935
            $this->dbHandler->quoteColumn('text')
936
        )->from(
937
            $this->dbHandler->quoteTable('ezurlalias_ml')
938
        )->where(
939
            $query->expr->lOr($hierarchyConditions)
940
        );
941
942
        $statement = $query->prepare();
943
        $statement->execute();
944
945
        $rows = $statement->fetchAll(\PDO::FETCH_ASSOC);
946
        $rowsMap = array();
947
        foreach ($rows as $row) {
948
            $rowsMap[$row['action']][] = $row;
949
        }
950
951
        if (count($rowsMap) !== count($hierarchyData)) {
952
            throw new \RuntimeException('The path is corrupted.');
953
        }
954
955
        $data = array();
956
        foreach ($hierarchyData as $levelData) {
957
            $data[] = $rowsMap[$levelData['action']];
958
        }
959
960
        return $data;
961
    }
962
963
    /**
964
     * Deletes single custom alias row matched by composite primary key.
965
     *
966
     * @param mixed $parentId
967
     * @param string $textMD5
968
     *
969
     * @return bool
970
     */
971
    public function removeCustomAlias($parentId, $textMD5)
972
    {
973
        /** @var $query \eZ\Publish\Core\Persistence\Database\DeleteQuery */
974
        $query = $this->dbHandler->createDeleteQuery();
975
        $query->deleteFrom(
976
            $this->dbHandler->quoteTable('ezurlalias_ml')
977
        )->where(
978
            $query->expr->lAnd(
979
                $query->expr->eq(
980
                    $this->dbHandler->quoteColumn('parent'),
981
                    $query->bindValue($parentId, null, \PDO::PARAM_INT)
982
                ),
983
                $query->expr->eq(
984
                    $this->dbHandler->quoteColumn('text_md5'),
985
                    $query->bindValue($textMD5, null, \PDO::PARAM_STR)
986
                ),
987
                $query->expr->eq(
988
                    $this->dbHandler->quoteColumn('is_alias'),
989
                    $query->bindValue(1, null, \PDO::PARAM_INT)
990
                )
991
            )
992
        );
993
        $statement = $query->prepare();
994
        $statement->execute();
995
996
        return $statement->rowCount() === 1 ?: false;
997
    }
998
999
    /**
1000
     * Deletes all rows with given $action and optionally $id.
1001
     *
1002
     * If $id is set only autogenerated entries will be removed.
1003
     *
1004
     * @param mixed $action
1005
     * @param mixed|null $id
1006
     *
1007
     * @return bool
1008
     */
1009 View Code Duplication
    public function remove($action, $id = null)
1010
    {
1011
        /** @var $query \eZ\Publish\Core\Persistence\Database\DeleteQuery */
1012
        $query = $this->dbHandler->createDeleteQuery();
1013
        $query->deleteFrom(
1014
            $this->dbHandler->quoteTable('ezurlalias_ml')
1015
        )->where(
1016
            $query->expr->eq(
1017
                $this->dbHandler->quoteColumn('action'),
1018
                $query->bindValue($action, null, \PDO::PARAM_STR)
1019
            )
1020
        );
1021
1022
        if ($id !== null) {
1023
            $query->where(
1024
                $query->expr->lAnd(
1025
                    $query->expr->eq(
1026
                        $this->dbHandler->quoteColumn('is_alias'),
1027
                        $query->bindValue(0, null, \PDO::PARAM_INT)
1028
                    ),
1029
                    $query->expr->eq(
1030
                        $this->dbHandler->quoteColumn('id'),
1031
                        $query->bindValue($id, null, \PDO::PARAM_INT)
1032
                    )
1033
                )
1034
            );
1035
        }
1036
1037
        $query->prepare()->execute();
1038
    }
1039
1040
    /**
1041
     * Loads all autogenerated entries with given $parentId with optionally included history entries.
1042
     *
1043
     * @param mixed $parentId
1044
     * @param bool $includeHistory
1045
     *
1046
     * @return array
1047
     */
1048 View Code Duplication
    public function loadAutogeneratedEntries($parentId, $includeHistory = false)
1049
    {
1050
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
1051
        $query = $this->dbHandler->createSelectQuery();
1052
        $query->select(
1053
            '*'
1054
        )->from(
1055
            $this->dbHandler->quoteTable('ezurlalias_ml')
1056
        )->where(
1057
            $query->expr->lAnd(
1058
                $query->expr->eq(
1059
                    $this->dbHandler->quoteColumn('parent'),
1060
                    $query->bindValue($parentId, null, \PDO::PARAM_INT)
1061
                ),
1062
                $query->expr->eq(
1063
                    $this->dbHandler->quoteColumn('action_type'),
1064
                    $query->bindValue('eznode', null, \PDO::PARAM_STR)
1065
                ),
1066
                $query->expr->eq(
1067
                    $this->dbHandler->quoteColumn('is_alias'),
1068
                    $query->bindValue(0, null, \PDO::PARAM_INT)
1069
                )
1070
            )
1071
        );
1072
1073
        if (!$includeHistory) {
1074
            $query->where(
1075
                $query->expr->eq(
1076
                    $this->dbHandler->quoteColumn('is_original'),
1077
                    $query->bindValue(1, null, \PDO::PARAM_INT)
1078
                )
1079
            );
1080
        }
1081
1082
        $statement = $query->prepare();
1083
        $statement->execute();
1084
1085
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
1086
    }
1087
}
1088