Completed
Push — 6.7 ( eacd1f...2a3684 )
by
unknown
25:16
created

DoctrineDatabase::internalLoadContentInfo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 14
rs 9.7998
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * File containing the DoctrineDatabase Content 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
namespace eZ\Publish\Core\Persistence\Legacy\Content\Gateway;
10
11
use Doctrine\DBAL\Connection;
12
use Doctrine\DBAL\Query\QueryBuilder as DoctrineQueryBuilder;
13
use eZ\Publish\Core\Persistence\Legacy\Content\Gateway;
14
use eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder;
15
use eZ\Publish\Core\Persistence\Database\DatabaseHandler;
16
use eZ\Publish\Core\Persistence\Database\UpdateQuery;
17
use eZ\Publish\Core\Persistence\Database\InsertQuery;
18
use eZ\Publish\Core\Persistence\Database\SelectQuery;
19
use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue;
20
use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator;
21
use eZ\Publish\SPI\Persistence\Content;
22
use eZ\Publish\SPI\Persistence\Content\CreateStruct;
23
use eZ\Publish\SPI\Persistence\Content\UpdateStruct;
24
use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct;
25
use eZ\Publish\SPI\Persistence\Content\ContentInfo;
26
use eZ\Publish\SPI\Persistence\Content\VersionInfo;
27
use eZ\Publish\SPI\Persistence\Content\Field;
28
use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct;
29
use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler;
30
use eZ\Publish\Core\Base\Exceptions\NotFoundException as NotFound;
31
use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo;
32
use DOMXPath;
33
use DOMDocument;
34
use PDO;
35
36
/**
37
 * Doctrine database based content gateway.
38
 */
39
class DoctrineDatabase extends Gateway
40
{
41
    /**
42
     * eZ Doctrine database handler.
43
     *
44
     * @var \eZ\Publish\Core\Persistence\Database\DatabaseHandler
45
     */
46
    protected $dbHandler;
47
48
    /**
49
     * The native Doctrine connection.
50
     *
51
     * Meant to be used to transition from eZ/Zeta interface to Doctrine.
52
     *
53
     * @var \Doctrine\DBAL\Connection
54
     */
55
    protected $connection;
56
57
    /**
58
     * Query builder.
59
     *
60
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder
61
     */
62
    protected $queryBuilder;
63
64
    /**
65
     * Caching language handler.
66
     *
67
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler
68
     */
69
    protected $languageHandler;
70
71
    /**
72
     * Language mask generator.
73
     *
74
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator
75
     */
76
    protected $languageMaskGenerator;
77
78
    /**
79
     * Creates a new gateway based on $db.
80
     *
81
     * @param \eZ\Publish\Core\Persistence\Database\DatabaseHandler $db
82
     * @param \Doctrine\DBAL\Connection $connection
83
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder $queryBuilder
84
     * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $languageHandler
85
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator $languageMaskGenerator
86
     */
87
    public function __construct(
88
        DatabaseHandler $db,
89
        Connection $connection,
90
        QueryBuilder $queryBuilder,
91
        LanguageHandler $languageHandler,
92
        LanguageMaskGenerator $languageMaskGenerator
93
    ) {
94
        $this->dbHandler = $db;
95
        $this->connection = $connection;
96
        $this->queryBuilder = $queryBuilder;
97
        $this->languageHandler = $languageHandler;
0 ignored issues
show
Documentation Bug introduced by
$languageHandler is of type object<eZ\Publish\SPI\Pe...ntent\Language\Handler>, but the property $languageHandler was declared to be of type object<eZ\Publish\Core\P...anguage\CachingHandler>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
98
        $this->languageMaskGenerator = $languageMaskGenerator;
99
    }
100
101
    /**
102
     * Get context definition for external storage layers.
103
     *
104
     * @return array
105
     */
106
    public function getContext()
107
    {
108
        return array(
109
            'identifier' => 'LegacyStorage',
110
            'connection' => $this->dbHandler,
111
        );
112
    }
113
114
    /**
115
     * Inserts a new content object.
116
     *
117
     * @param \eZ\Publish\SPI\Persistence\Content\CreateStruct $struct
118
     * @param mixed $currentVersionNo
119
     *
120
     * @return int ID
121
     */
122
    public function insertContentObject(CreateStruct $struct, $currentVersionNo = 1)
123
    {
124
        $initialLanguageId = !empty($struct->mainLanguageId) ? $struct->mainLanguageId : $struct->initialLanguageId;
125
        $initialLanguageCode = $this->languageHandler->load($initialLanguageId)->languageCode;
126
127
        if (isset($struct->name[$initialLanguageCode])) {
128
            $name = $struct->name[$initialLanguageCode];
129
        } else {
130
            $name = '';
131
        }
132
133
        $q = $this->dbHandler->createInsertQuery();
134
        $q->insertInto(
135
            $this->dbHandler->quoteTable('ezcontentobject')
136
        )->set(
137
            $this->dbHandler->quoteColumn('id'),
138
            $this->dbHandler->getAutoIncrementValue('ezcontentobject', 'id')
139
        )->set(
140
            $this->dbHandler->quoteColumn('current_version'),
141
            $q->bindValue($currentVersionNo, null, \PDO::PARAM_INT)
142
        )->set(
143
            $this->dbHandler->quoteColumn('name'),
144
            $q->bindValue($name, null, \PDO::PARAM_STR)
145
        )->set(
146
            $this->dbHandler->quoteColumn('contentclass_id'),
147
            $q->bindValue($struct->typeId, null, \PDO::PARAM_INT)
148
        )->set(
149
            $this->dbHandler->quoteColumn('section_id'),
150
            $q->bindValue($struct->sectionId, null, \PDO::PARAM_INT)
151
        )->set(
152
            $this->dbHandler->quoteColumn('owner_id'),
153
            $q->bindValue($struct->ownerId, null, \PDO::PARAM_INT)
154
        )->set(
155
            $this->dbHandler->quoteColumn('initial_language_id'),
156
            $q->bindValue($initialLanguageId, null, \PDO::PARAM_INT)
157
        )->set(
158
            $this->dbHandler->quoteColumn('remote_id'),
159
            $q->bindValue($struct->remoteId, null, \PDO::PARAM_STR)
160
        )->set(
161
            $this->dbHandler->quoteColumn('modified'),
162
            $q->bindValue(0, null, \PDO::PARAM_INT)
163
        )->set(
164
            $this->dbHandler->quoteColumn('published'),
165
            $q->bindValue(0, null, \PDO::PARAM_INT)
166
        )->set(
167
            $this->dbHandler->quoteColumn('status'),
168
            $q->bindValue(ContentInfo::STATUS_DRAFT, null, \PDO::PARAM_INT)
169
        )->set(
170
            $this->dbHandler->quoteColumn('language_mask'),
171
            $q->bindValue(
172
                $this->generateLanguageMask(
173
                    $struct->fields,
174
                    $initialLanguageCode,
175
                    $struct->alwaysAvailable
176
                ),
177
                null,
178
                \PDO::PARAM_INT
179
            )
180
        );
181
182
        $q->prepare()->execute();
183
184
        return $this->dbHandler->lastInsertId(
185
            $this->dbHandler->getSequenceName('ezcontentobject', 'id')
186
        );
187
    }
188
189
    /**
190
     * Generates a language mask for $version.
191
     *
192
     * @param \eZ\Publish\SPI\Persistence\Content\Field[] $fields
193
     * @param string $initialLanguageCode
194
     * @param bool $alwaysAvailable
195
     *
196
     * @return int
197
     */
198
    protected function generateLanguageMask(array $fields, $initialLanguageCode, $alwaysAvailable)
199
    {
200
        $languages = array($initialLanguageCode => true);
201
        foreach ($fields as $field) {
202
            if (isset($languages[$field->languageCode])) {
203
                continue;
204
            }
205
206
            $languages[$field->languageCode] = true;
207
        }
208
209
        if ($alwaysAvailable) {
210
            $languages['always-available'] = true;
211
        }
212
213
        return $this->languageMaskGenerator->generateLanguageMask($languages);
214
    }
215
216
    /**
217
     * Inserts a new version.
218
     *
219
     * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo
220
     * @param \eZ\Publish\SPI\Persistence\Content\Field[] $fields
221
     *
222
     * @return int ID
223
     */
224
    public function insertVersion(VersionInfo $versionInfo, array $fields)
225
    {
226
        /** @var $q \eZ\Publish\Core\Persistence\Database\InsertQuery */
227
        $q = $this->dbHandler->createInsertQuery();
228
        $q->insertInto(
229
            $this->dbHandler->quoteTable('ezcontentobject_version')
230
        )->set(
231
            $this->dbHandler->quoteColumn('id'),
232
            $this->dbHandler->getAutoIncrementValue('ezcontentobject_version', 'id')
233
        )->set(
234
            $this->dbHandler->quoteColumn('version'),
235
            $q->bindValue($versionInfo->versionNo, null, \PDO::PARAM_INT)
236
        )->set(
237
            $this->dbHandler->quoteColumn('modified'),
238
            $q->bindValue($versionInfo->modificationDate, null, \PDO::PARAM_INT)
239
        )->set(
240
            $this->dbHandler->quoteColumn('creator_id'),
241
            $q->bindValue($versionInfo->creatorId, null, \PDO::PARAM_INT)
242
        )->set(
243
            $this->dbHandler->quoteColumn('created'),
244
            $q->bindValue($versionInfo->creationDate, null, \PDO::PARAM_INT)
245
        )->set(
246
            $this->dbHandler->quoteColumn('status'),
247
            $q->bindValue($versionInfo->status, null, \PDO::PARAM_INT)
248
        )->set(
249
            $this->dbHandler->quoteColumn('initial_language_id'),
250
            $q->bindValue(
251
                $this->languageHandler->loadByLanguageCode($versionInfo->initialLanguageCode)->id,
252
                null,
253
                \PDO::PARAM_INT
254
            )
255
        )->set(
256
            $this->dbHandler->quoteColumn('contentobject_id'),
257
            $q->bindValue($versionInfo->contentInfo->id, null, \PDO::PARAM_INT)
258
        )->set(
259
            // As described in field mapping document
260
            $this->dbHandler->quoteColumn('workflow_event_pos'),
261
            $q->bindValue(0, null, \PDO::PARAM_INT)
262
        )->set(
263
            $this->dbHandler->quoteColumn('language_mask'),
264
            $q->bindValue(
265
                $this->generateLanguageMask(
266
                    $fields,
267
                    $versionInfo->initialLanguageCode,
268
                    $versionInfo->contentInfo->alwaysAvailable
269
                ),
270
                null,
271
                \PDO::PARAM_INT
272
            )
273
        );
274
275
        $q->prepare()->execute();
276
277
        return $this->dbHandler->lastInsertId(
278
            $this->dbHandler->getSequenceName('ezcontentobject_version', 'id')
279
        );
280
    }
281
282
    /**
283
     * Updates an existing content identified by $contentId in respect to $struct.
284
     *
285
     * @param int $contentId
286
     * @param \eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct $struct
287
     * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $prePublishVersionInfo Provided on publish
288
     */
289
    public function updateContent($contentId, MetadataUpdateStruct $struct, VersionInfo $prePublishVersionInfo = null)
290
    {
291
        $q = $this->dbHandler->createUpdateQuery();
292
        $q->update($this->dbHandler->quoteTable('ezcontentobject'));
293
294
        if (isset($struct->name)) {
295
            $q->set(
296
                $this->dbHandler->quoteColumn('name'),
297
                $q->bindValue($struct->name, null, \PDO::PARAM_STR)
298
            );
299
        }
300
        if (isset($struct->mainLanguageId)) {
301
            $q->set(
302
                $this->dbHandler->quoteColumn('initial_language_id'),
303
                $q->bindValue($struct->mainLanguageId, null, \PDO::PARAM_INT)
304
            );
305
        }
306
        if (isset($struct->modificationDate)) {
307
            $q->set(
308
                $this->dbHandler->quoteColumn('modified'),
309
                $q->bindValue($struct->modificationDate, null, \PDO::PARAM_INT)
310
            );
311
        }
312
        if (isset($struct->ownerId)) {
313
            $q->set(
314
                $this->dbHandler->quoteColumn('owner_id'),
315
                $q->bindValue($struct->ownerId, null, \PDO::PARAM_INT)
316
            );
317
        }
318
        if (isset($struct->publicationDate)) {
319
            $q->set(
320
                $this->dbHandler->quoteColumn('published'),
321
                $q->bindValue($struct->publicationDate, null, \PDO::PARAM_INT)
322
            );
323
        }
324
        if (isset($struct->remoteId)) {
325
            $q->set(
326
                $this->dbHandler->quoteColumn('remote_id'),
327
                $q->bindValue($struct->remoteId, null, \PDO::PARAM_STR)
328
            );
329
        }
330
        if ($prePublishVersionInfo !== null) {
331
            $mask = 0;
332
333
            if (isset($struct->alwaysAvailable)) {
334
                $mask |= $struct->alwaysAvailable ? 1 : 0;
335
            } else {
336
                $mask |= $prePublishVersionInfo->contentInfo->alwaysAvailable ? 1 : 0;
337
            }
338
339
            foreach ($prePublishVersionInfo->languageIds as $languageId) {
340
                $mask |= $languageId;
341
            }
342
343
            $q->set(
344
                $this->dbHandler->quoteColumn('language_mask'),
345
                $q->bindValue($mask, null, \PDO::PARAM_INT)
346
            );
347
        }
348
        $q->where(
349
            $q->expr->eq(
350
                $this->dbHandler->quoteColumn('id'),
351
                $q->bindValue($contentId, null, \PDO::PARAM_INT)
352
            )
353
        );
354
        $q->prepare()->execute();
355
356
        // Handle alwaysAvailable flag update separately as it's a more complex task and has impact on several tables
357
        if (isset($struct->alwaysAvailable) || isset($struct->mainLanguageId)) {
358
            $this->updateAlwaysAvailableFlag($contentId, $struct->alwaysAvailable);
359
        }
360
    }
361
362
    /**
363
     * Updates version $versionNo for content identified by $contentId, in respect to $struct.
364
     *
365
     * @param int $contentId
366
     * @param int $versionNo
367
     * @param \eZ\Publish\SPI\Persistence\Content\UpdateStruct $struct
368
     */
369
    public function updateVersion($contentId, $versionNo, UpdateStruct $struct)
370
    {
371
        $q = $this->dbHandler->createUpdateQuery();
372
        $q->update(
373
            $this->dbHandler->quoteTable('ezcontentobject_version')
374
        )->set(
375
            $this->dbHandler->quoteColumn('creator_id'),
376
            $q->bindValue($struct->creatorId, null, \PDO::PARAM_INT)
377
        )->set(
378
            $this->dbHandler->quoteColumn('modified'),
379
            $q->bindValue($struct->modificationDate, null, \PDO::PARAM_INT)
380
        )->set(
381
            $this->dbHandler->quoteColumn('initial_language_id'),
382
            $q->bindValue($struct->initialLanguageId, null, \PDO::PARAM_INT)
383
        )->set(
384
            $this->dbHandler->quoteColumn('language_mask'),
385
            $q->expr->bitOr(
386
                $this->dbHandler->quoteColumn('language_mask'),
387
                $q->bindValue(
388
                    $this->generateLanguageMask(
389
                        $struct->fields,
390
                        $this->languageHandler->load($struct->initialLanguageId)->languageCode,
391
                        false
392
                    ),
393
                    null,
394
                    \PDO::PARAM_INT
395
                )
396
            )
397
        )->where(
398
            $q->expr->lAnd(
399
                $q->expr->eq(
400
                    $this->dbHandler->quoteColumn('contentobject_id'),
401
                    $q->bindValue($contentId, null, \PDO::PARAM_INT)
402
                ),
403
                $q->expr->eq(
404
                    $this->dbHandler->quoteColumn('version'),
405
                    $q->bindValue($versionNo, null, \PDO::PARAM_INT)
406
                )
407
            )
408
        );
409
        $q->prepare()->execute();
410
    }
411
412
    /**
413
     * Updates "always available" flag for Content identified by $contentId, in respect to
414
     * Content's current main language and optionally new $alwaysAvailable state.
415
     *
416
     * @param int $contentId
417
     * @param bool|null $alwaysAvailable New "always available" value or null if not defined
418
     */
419
    public function updateAlwaysAvailableFlag($contentId, $alwaysAvailable = null)
420
    {
421
        // We will need to know some info on the current language mask to update the flag
422
        // everywhere needed
423
        $contentInfoRow = $this->loadContentInfo($contentId);
424
        if (!isset($alwaysAvailable)) {
425
            $alwaysAvailable = (bool)$contentInfoRow['language_mask'] & 1;
426
        }
427
428
        /** @var $q \eZ\Publish\Core\Persistence\Database\UpdateQuery */
429
        $q = $this->dbHandler->createUpdateQuery();
430
        $q
431
            ->update($this->dbHandler->quoteTable('ezcontentobject'))
432
            ->set(
433
                $this->dbHandler->quoteColumn('language_mask'),
434
                $alwaysAvailable ?
435
                    $q->expr->bitOr($this->dbHandler->quoteColumn('language_mask'), 1) :
436
                    $q->expr->bitAnd($this->dbHandler->quoteColumn('language_mask'), -2)
437
            )
438
            ->where(
439
                $q->expr->eq(
440
                    $this->dbHandler->quoteColumn('id'),
441
                    $q->bindValue($contentId, null, \PDO::PARAM_INT)
442
                )
443
            );
444
        $q->prepare()->execute();
445
446
        // Now we need to update ezcontentobject_name
447
        /** @var $qName \eZ\Publish\Core\Persistence\Database\UpdateQuery */
448
        $qName = $this->dbHandler->createUpdateQuery();
449
        $qName
450
            ->update($this->dbHandler->quoteTable('ezcontentobject_name'))
451
            ->set(
452
                $this->dbHandler->quoteColumn('language_id'),
453
                $alwaysAvailable ?
454
                    $qName->expr->bitOr($this->dbHandler->quoteColumn('language_id'), 1) :
455
                    $qName->expr->bitAnd($this->dbHandler->quoteColumn('language_id'), -2)
456
            )
457
            ->where(
458
                $qName->expr->lAnd(
459
                    $qName->expr->eq(
460
                        $this->dbHandler->quoteColumn('contentobject_id'),
461
                        $qName->bindValue($contentId, null, \PDO::PARAM_INT)
462
                    ),
463
                    $qName->expr->eq(
464
                        $this->dbHandler->quoteColumn('content_version'),
465
                        $qName->bindValue(
466
                            $contentInfoRow['current_version'],
467
                            null,
468
                            \PDO::PARAM_INT
469
                        )
470
                    )
471
                )
472
            );
473
        $qName->prepare()->execute();
474
475
        // Now update ezcontentobject_attribute for current version
476
        // Create update query that will be reused
477
        /** @var $qAttr \eZ\Publish\Core\Persistence\Database\UpdateQuery */
478
        $qAttr = $this->dbHandler->createUpdateQuery();
479
        $qAttr
480
            ->update($this->dbHandler->quoteTable('ezcontentobject_attribute'))
481
            ->where(
482
                $qAttr->expr->lAnd(
483
                    $qAttr->expr->eq(
484
                        $this->dbHandler->quoteColumn('contentobject_id'),
485
                        $qAttr->bindValue($contentId, null, \PDO::PARAM_INT)
486
                    ),
487
                    $qAttr->expr->eq(
488
                        $this->dbHandler->quoteColumn('version'),
489
                        $qAttr->bindValue(
490
                            $contentInfoRow['current_version'],
491
                            null,
492
                            \PDO::PARAM_INT
493
                        )
494
                    )
495
                )
496
            );
497
498
        // If there is only a single language, update all fields and return
499
        if (!$this->languageMaskGenerator->isLanguageMaskComposite($contentInfoRow['language_mask'])) {
500
            $qAttr->set(
501
                $this->dbHandler->quoteColumn('language_id'),
502
                $alwaysAvailable ?
503
                    $qAttr->expr->bitOr($this->dbHandler->quoteColumn('language_id'), 1) :
504
                    $qAttr->expr->bitAnd($this->dbHandler->quoteColumn('language_id'), -2)
505
            );
506
            $qAttr->prepare()->execute();
507
508
            return;
509
        }
510
511
        // Otherwise:
512
        // 1. Remove always available flag on all fields
513
        $qAttr->set(
514
            $this->dbHandler->quoteColumn('language_id'),
515
            $qAttr->expr->bitAnd($this->dbHandler->quoteColumn('language_id'), -2)
516
        );
517
        $qAttr->prepare()->execute();
518
519
        // 2. If Content is always available set the flag only on fields in main language
520
        if ($alwaysAvailable) {
521
            $qAttr->set(
522
                $this->dbHandler->quoteColumn('language_id'),
523
                $qAttr->expr->bitOr($this->dbHandler->quoteColumn('language_id'), 1)
524
            );
525
            $qAttr->where(
526
                $qAttr->expr->gt(
527
                    $qAttr->expr->bitAnd(
528
                        $this->dbHandler->quoteColumn('language_id'),
529
                        $qAttr->bindValue($contentInfoRow['initial_language_id'], null, PDO::PARAM_INT)
530
                    ),
531
                    $qAttr->bindValue(0, null, PDO::PARAM_INT)
532
                )
533
            );
534
            $qAttr->prepare()->execute();
535
        }
536
    }
537
538
    /**
539
     * Sets the status of the version identified by $contentId and $version to $status.
540
     *
541
     * The $status can be one of STATUS_DRAFT, STATUS_PUBLISHED, STATUS_ARCHIVED
542
     *
543
     * @param int $contentId
544
     * @param int $version
545
     * @param int $status
546
     *
547
     * @return bool
548
     */
549
    public function setStatus($contentId, $version, $status)
550
    {
551
        $q = $this->dbHandler->createUpdateQuery();
552
        $q->update(
553
            $this->dbHandler->quoteTable('ezcontentobject_version')
554
        )->set(
555
            $this->dbHandler->quoteColumn('status'),
556
            $q->bindValue($status, null, \PDO::PARAM_INT)
557
        )->set(
558
            $this->dbHandler->quoteColumn('modified'),
559
            $q->bindValue(time(), null, \PDO::PARAM_INT)
560
        )->where(
561
            $q->expr->lAnd(
562
                $q->expr->eq(
563
                    $this->dbHandler->quoteColumn('contentobject_id'),
564
                    $q->bindValue($contentId, null, \PDO::PARAM_INT)
565
                ),
566
                $q->expr->eq(
567
                    $this->dbHandler->quoteColumn('version'),
568
                    $q->bindValue($version, null, \PDO::PARAM_INT)
569
                )
570
            )
571
        );
572
        $statement = $q->prepare();
573
        $statement->execute();
574
575
        if ((bool)$statement->rowCount() === false) {
576
            return false;
577
        }
578
579
        if ($status !== APIVersionInfo::STATUS_PUBLISHED) {
580
            return true;
581
        }
582
583
        // If the version's status is PUBLISHED, we set the content to published status as well
584
        $q = $this->dbHandler->createUpdateQuery();
585
        $q->update(
586
            $this->dbHandler->quoteTable('ezcontentobject')
587
        )->set(
588
            $this->dbHandler->quoteColumn('status'),
589
            $q->bindValue(ContentInfo::STATUS_PUBLISHED, null, \PDO::PARAM_INT)
590
        )->set(
591
            $this->dbHandler->quoteColumn('current_version'),
592
            $q->bindValue($version, null, \PDO::PARAM_INT)
593
        )->where(
594
            $q->expr->eq(
595
                $this->dbHandler->quoteColumn('id'),
596
                $q->bindValue($contentId, null, \PDO::PARAM_INT)
597
            )
598
        );
599
        $statement = $q->prepare();
600
        $statement->execute();
601
602
        return (bool)$statement->rowCount();
603
    }
604
605
    /**
606
     * Inserts a new field.
607
     *
608
     * Only used when a new field is created (i.e. a new object or a field in a
609
     * new language!). After that, field IDs need to stay the same, only the
610
     * version number changes.
611
     *
612
     * @param \eZ\Publish\SPI\Persistence\Content $content
613
     * @param \eZ\Publish\SPI\Persistence\Content\Field $field
614
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value
615
     *
616
     * @return int ID
617
     */
618
    public function insertNewField(Content $content, Field $field, StorageFieldValue $value)
619
    {
620
        $q = $this->dbHandler->createInsertQuery();
621
622
        $this->setInsertFieldValues($q, $content, $field, $value);
623
624
        // Insert with auto increment ID
625
        $q->set(
626
            $this->dbHandler->quoteColumn('id'),
627
            $this->dbHandler->getAutoIncrementValue('ezcontentobject_attribute', 'id')
628
        );
629
630
        $q->prepare()->execute();
631
632
        return $this->dbHandler->lastInsertId(
633
            $this->dbHandler->getSequenceName('ezcontentobject_attribute', 'id')
634
        );
635
    }
636
637
    /**
638
     * Inserts an existing field.
639
     *
640
     * Used to insert a field with an exsting ID but a new version number.
641
     *
642
     * @param Content $content
643
     * @param Field $field
644
     * @param StorageFieldValue $value
645
     */
646
    public function insertExistingField(Content $content, Field $field, StorageFieldValue $value)
647
    {
648
        $q = $this->dbHandler->createInsertQuery();
649
650
        $this->setInsertFieldValues($q, $content, $field, $value);
651
652
        $q->set(
653
            $this->dbHandler->quoteColumn('id'),
654
            $q->bindValue($field->id, null, \PDO::PARAM_INT)
655
        );
656
657
        $q->prepare()->execute();
658
    }
659
660
    /**
661
     * Sets field (ezcontentobject_attribute) values to the given query.
662
     *
663
     * @param \eZ\Publish\Core\Persistence\Database\InsertQuery $q
664
     * @param Content $content
665
     * @param Field $field
666
     * @param StorageFieldValue $value
667
     */
668
    protected function setInsertFieldValues(InsertQuery $q, Content $content, Field $field, StorageFieldValue $value)
669
    {
670
        $q->insertInto(
671
            $this->dbHandler->quoteTable('ezcontentobject_attribute')
672
        )->set(
673
            $this->dbHandler->quoteColumn('contentobject_id'),
674
            $q->bindValue($content->versionInfo->contentInfo->id, null, \PDO::PARAM_INT)
675
        )->set(
676
            $this->dbHandler->quoteColumn('contentclassattribute_id'),
677
            $q->bindValue($field->fieldDefinitionId, null, \PDO::PARAM_INT)
678
        )->set(
679
            $this->dbHandler->quoteColumn('data_type_string'),
680
            $q->bindValue($field->type)
681
        )->set(
682
            $this->dbHandler->quoteColumn('language_code'),
683
            $q->bindValue($field->languageCode)
684
        )->set(
685
            $this->dbHandler->quoteColumn('version'),
686
            $q->bindValue($field->versionNo, null, \PDO::PARAM_INT)
687
        )->set(
688
            $this->dbHandler->quoteColumn('data_float'),
689
            $q->bindValue($value->dataFloat)
690
        )->set(
691
            $this->dbHandler->quoteColumn('data_int'),
692
            $q->bindValue($value->dataInt, null, \PDO::PARAM_INT)
693
        )->set(
694
            $this->dbHandler->quoteColumn('data_text'),
695
            $q->bindValue($value->dataText)
696
        )->set(
697
            $this->dbHandler->quoteColumn('sort_key_int'),
698
            $q->bindValue($value->sortKeyInt, null, \PDO::PARAM_INT)
699
        )->set(
700
            $this->dbHandler->quoteColumn('sort_key_string'),
701
            $q->bindValue(mb_substr($value->sortKeyString, 0, 255))
702
        )->set(
703
            $this->dbHandler->quoteColumn('language_id'),
704
            $q->bindValue(
705
                $this->languageMaskGenerator->generateLanguageIndicator(
706
                    $field->languageCode,
707
                    $this->isLanguageAlwaysAvailable($content, $field->languageCode)
708
                ),
709
                null,
710
                \PDO::PARAM_INT
711
            )
712
        );
713
    }
714
715
    /**
716
     * Checks if $languageCode is always available in $content.
717
     *
718
     * @param \eZ\Publish\SPI\Persistence\Content $content
719
     * @param string $languageCode
720
     *
721
     * @return bool
722
     */
723
    protected function isLanguageAlwaysAvailable(Content $content, $languageCode)
724
    {
725
        return
726
            $content->versionInfo->contentInfo->alwaysAvailable &&
727
            $content->versionInfo->contentInfo->mainLanguageCode === $languageCode
728
        ;
729
    }
730
731
    /**
732
     * Updates an existing field.
733
     *
734
     * @param Field $field
735
     * @param StorageFieldValue $value
736
     */
737
    public function updateField(Field $field, StorageFieldValue $value)
738
    {
739
        // Note, no need to care for language_id here, since Content->$alwaysAvailable
740
        // cannot change on update
741
        $q = $this->dbHandler->createUpdateQuery();
742
        $this->setFieldUpdateValues($q, $value);
743
        $q->where(
744
            $q->expr->lAnd(
745
                $q->expr->eq(
746
                    $this->dbHandler->quoteColumn('id'),
747
                    $q->bindValue($field->id, null, \PDO::PARAM_INT)
748
                ),
749
                $q->expr->eq(
750
                    $this->dbHandler->quoteColumn('version'),
751
                    $q->bindValue($field->versionNo, null, \PDO::PARAM_INT)
752
                )
753
            )
754
        );
755
        $q->prepare()->execute();
756
    }
757
758
    /**
759
     * Sets update fields for $value on $q.
760
     *
761
     * @param \eZ\Publish\Core\Persistence\Database\UpdateQuery $q
762
     * @param StorageFieldValue $value
763
     */
764
    protected function setFieldUpdateValues(UpdateQuery $q, StorageFieldValue $value)
765
    {
766
        $q->update(
767
            $this->dbHandler->quoteTable('ezcontentobject_attribute')
768
        )->set(
769
            $this->dbHandler->quoteColumn('data_float'),
770
            $q->bindValue($value->dataFloat)
771
        )->set(
772
            $this->dbHandler->quoteColumn('data_int'),
773
            $q->bindValue($value->dataInt, null, \PDO::PARAM_INT)
774
        )->set(
775
            $this->dbHandler->quoteColumn('data_text'),
776
            $q->bindValue($value->dataText)
777
        )->set(
778
            $this->dbHandler->quoteColumn('sort_key_int'),
779
            $q->bindValue($value->sortKeyInt, null, \PDO::PARAM_INT)
780
        )->set(
781
            $this->dbHandler->quoteColumn('sort_key_string'),
782
            $q->bindValue(mb_substr($value->sortKeyString, 0, 255))
783
        );
784
    }
785
786
    /**
787
     * Updates an existing, non-translatable field.
788
     *
789
     * @param \eZ\Publish\SPI\Persistence\Content\Field $field
790
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value
791
     * @param int $contentId
792
     */
793
    public function updateNonTranslatableField(
794
        Field $field,
795
        StorageFieldValue $value,
796
        $contentId
797
    ) {
798
        // Note, no need to care for language_id here, since Content->$alwaysAvailable
799
        // cannot change on update
800
        $q = $this->dbHandler->createUpdateQuery();
801
        $this->setFieldUpdateValues($q, $value);
802
        $q->where(
803
            $q->expr->lAnd(
804
                $q->expr->eq(
805
                    $this->dbHandler->quoteColumn('contentclassattribute_id'),
806
                    $q->bindValue($field->fieldDefinitionId, null, \PDO::PARAM_INT)
807
                ),
808
                $q->expr->eq(
809
                    $this->dbHandler->quoteColumn('contentobject_id'),
810
                    $q->bindValue($contentId, null, \PDO::PARAM_INT)
811
                ),
812
                $q->expr->eq(
813
                    $this->dbHandler->quoteColumn('version'),
814
                    $q->bindValue($field->versionNo, null, \PDO::PARAM_INT)
815
                )
816
            )
817
        );
818
        $q->prepare()->execute();
819
    }
820
821
    /**
822
     * Loads data for a content object.
823
     *
824
     * Returns an array with the relevant data.
825
     *
826
     * @param mixed $contentId
827
     * @param mixed $version
828
     * @param string[] $translations
829
     *
830
     * @return array
831
     */
832 View Code Duplication
    public function load($contentId, $version, array $translations = null)
833
    {
834
        $query = $this->queryBuilder->createFindQuery($translations);
835
        $query->where(
836
            $query->expr->lAnd(
837
                $query->expr->eq(
838
                    $this->dbHandler->quoteColumn('id', 'ezcontentobject'),
839
                    $query->bindValue($contentId)
840
                ),
841
                $query->expr->eq(
842
                    $this->dbHandler->quoteColumn('version', 'ezcontentobject_version'),
843
                    $query->bindValue($version)
844
                )
845
            )
846
        );
847
        $statement = $query->prepare();
848
        $statement->execute();
849
850
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
851
    }
852
853
    /**
854
     * {@inheritdoc}
855
     */
856
    public function loadContentList(array $contentIds, array $translations = null)
857
    {
858
        return $this->internalLoadContent($contentIds, null, $translations);
0 ignored issues
show
Bug introduced by
It seems like $translations defined by parameter $translations on line 856 can also be of type array; however, eZ\Publish\Core\Persiste...::internalLoadContent() does only seem to accept null|array<integer,string>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
859
    }
860
861
    /**
862
     * @see loadContentList()
863
     *
864
     * @param array $contentIds
865
     * @param int $version
866
     * @param string[]|null $translations
867
     *
868
     * @return array
869
     */
870
    private function internalLoadContent(array $contentIds, $version = null, array $translations = null)
871
    {
872
        $queryBuilder = $this->connection->createQueryBuilder();
873
        $expr = $queryBuilder->expr();
874
        $queryBuilder
875
            ->select(
876
                'c.id AS ezcontentobject_id',
877
                'c.contentclass_id AS ezcontentobject_contentclass_id',
878
                'c.section_id AS ezcontentobject_section_id',
879
                'c.owner_id AS ezcontentobject_owner_id',
880
                'c.remote_id AS ezcontentobject_remote_id',
881
                'c.current_version AS ezcontentobject_current_version',
882
                'c.initial_language_id AS ezcontentobject_initial_language_id',
883
                'c.modified AS ezcontentobject_modified',
884
                'c.published AS ezcontentobject_published',
885
                'c.status AS ezcontentobject_status',
886
                'c.name AS ezcontentobject_name',
887
                'c.language_mask AS ezcontentobject_language_mask',
888
                'v.id AS ezcontentobject_version_id',
889
                'v.version AS ezcontentobject_version_version',
890
                'v.modified AS ezcontentobject_version_modified',
891
                'v.creator_id AS ezcontentobject_version_creator_id',
892
                'v.created AS ezcontentobject_version_created',
893
                'v.status AS ezcontentobject_version_status',
894
                'v.language_mask AS ezcontentobject_version_language_mask',
895
                'v.initial_language_id AS ezcontentobject_version_initial_language_id',
896
                'a.id AS ezcontentobject_attribute_id',
897
                'a.contentclassattribute_id AS ezcontentobject_attribute_contentclassattribute_id',
898
                'a.data_type_string AS ezcontentobject_attribute_data_type_string',
899
                'a.language_code AS ezcontentobject_attribute_language_code',
900
                'a.language_id AS ezcontentobject_attribute_language_id',
901
                'a.data_float AS ezcontentobject_attribute_data_float',
902
                'a.data_int AS ezcontentobject_attribute_data_int',
903
                'a.data_text AS ezcontentobject_attribute_data_text',
904
                'a.sort_key_int AS ezcontentobject_attribute_sort_key_int',
905
                'a.sort_key_string AS ezcontentobject_attribute_sort_key_string',
906
                't.main_node_id AS ezcontentobject_tree_main_node_id'
907
            )
908
            ->from('ezcontentobject', 'c')
909
            ->innerJoin(
910
                'c',
911
                'ezcontentobject_version',
912
                'v',
913
                $expr->andX(
914
                    $expr->eq('c.id', 'v.contentobject_id'),
915
                    $expr->eq('v.version', $version ?: 'c.current_version')
916
                )
917
            )
918
            ->innerJoin(
919
                'v',
920
                'ezcontentobject_attribute',
921
                'a',
922
                $expr->andX(
923
                    $expr->eq('v.contentobject_id', 'a.contentobject_id'),
924
                    $expr->eq('v.version', 'a.version')
925
                )
926
            )
927
            ->leftJoin(
928
                'c',
929
                'ezcontentobject_tree',
930
                't',
931
                $expr->andX(
932
                    $expr->eq('c.id', 't.contentobject_id'),
933
                    $expr->eq('t.node_id', 't.main_node_id')
934
                )
935
            );
936
937
        $queryBuilder->where(
938
            $expr->in(
939
                'c.id',
940
                $queryBuilder->createNamedParameter($contentIds, Connection::PARAM_INT_ARRAY)
941
            )
942
        );
943
944
        if (!empty($translations)) {
945
            $queryBuilder->andWhere(
946
                $expr->in(
947
                    'a.language_code',
948
                    $queryBuilder->createNamedParameter($translations, Connection::PARAM_STR_ARRAY)
949
                )
950
            );
951
        }
952
953
        return $queryBuilder->execute()->fetchAll(PDO::FETCH_ASSOC);
954
    }
955
956
    /**
957
     * @see loadContentInfo(), loadContentInfoByRemoteId(), loadContentInfoList(), loadContentInfoByLocationId()
958
     *
959
     * @param \Doctrine\DBAL\Query\QueryBuilder $query
960
     *
961
     * @return array
962
     */
963
    private function internalLoadContentInfo(DoctrineQueryBuilder $query)
964
    {
965
        $query
966
            ->select('c.*', 't.main_node_id AS ezcontentobject_tree_main_node_id')
967
            ->from('ezcontentobject', 'c')
968
            ->leftJoin(
969
                'c',
970
                'ezcontentobject_tree',
971
                't',
972
                'c.id = t.contentobject_id AND t.node_id = t.main_node_id'
973
            );
974
975
        return $query->execute()->fetchAll();
976
    }
977
978
    /**
979
     * Loads info for content identified by $contentId.
980
     * Will basically return a hash containing all field values for ezcontentobject table plus some additional keys:
981
     *  - always_available => Boolean indicating if content's language mask contains alwaysAvailable bit field
982
     *  - main_language_code => Language code for main (initial) language. E.g. "eng-GB".
983
     *
984
     * @param int $contentId
985
     *
986
     * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException
987
     *
988
     * @return array
989
     */
990 View Code Duplication
    public function loadContentInfo($contentId)
991
    {
992
        $query = $this->connection->createQueryBuilder();
993
        $query->where('c.id = :id')
994
              ->setParameter('id', $contentId, PDO::PARAM_INT);
995
996
        $results = $this->internalLoadContentInfo($query);
997
        if (empty($results)) {
998
            throw new NotFound('content', "id: $contentId");
999
        }
1000
1001
        return $results[0];
1002
    }
1003
1004
    public function loadContentInfoList(array $contentIds)
1005
    {
1006
        $query = $this->connection->createQueryBuilder();
1007
        $query->where('c.id IN (:ids)')
1008
              ->setParameter('ids', $contentIds, Connection::PARAM_INT_ARRAY);
1009
1010
        return $this->internalLoadContentInfo($query);
1011
    }
1012
1013
    /**
1014
     * Loads info for a content object identified by its remote ID.
1015
     *
1016
     * Returns an array with the relevant data.
1017
     *
1018
     * @param mixed $remoteId
1019
     *
1020
     * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException
1021
     *
1022
     * @return array
1023
     */
1024 View Code Duplication
    public function loadContentInfoByRemoteId($remoteId)
1025
    {
1026
        $query = $this->connection->createQueryBuilder();
1027
        $query->where('c.remote_id = :id')
1028
              ->setParameter('id', $remoteId, PDO::PARAM_STR);
1029
1030
        $results = $this->internalLoadContentInfo($query);
1031
        if (empty($results)) {
1032
            throw new NotFound('content', "remote_id: $remoteId");
1033
        }
1034
1035
        return $results[0];
1036
    }
1037
1038
    /**
1039
     * Loads info for a content object identified by its location ID (node ID).
1040
     *
1041
     * Returns an array with the relevant data.
1042
     *
1043
     * @param int $locationId
1044
     *
1045
     * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException
1046
     *
1047
     * @return array
1048
     */
1049 View Code Duplication
    public function loadContentInfoByLocationId($locationId)
1050
    {
1051
        $query = $this->connection->createQueryBuilder();
1052
        $query->where('t.main_node_id = :id')
1053
              ->setParameter('id', $locationId, PDO::PARAM_INT);
1054
1055
        $results = $this->internalLoadContentInfo($query);
1056
        if (empty($results)) {
1057
            throw new NotFound('content', "main_node_id: $locationId");
1058
        }
1059
1060
        return $results[0];
1061
    }
1062
1063
    /**
1064
     * Loads version info for content identified by $contentId and $versionNo.
1065
     * Will basically return a hash containing all field values from ezcontentobject_version table plus following keys:
1066
     *  - names => Hash of content object names. Key is the language code, value is the name.
1067
     *  - languages => Hash of language ids. Key is the language code (e.g. "eng-GB"), value is the language numeric id without the always available bit.
1068
     *  - initial_language_code => Language code for initial language in this version.
1069
     *
1070
     * @param int $contentId
1071
     * @param int $versionNo
1072
     *
1073
     * @return array
1074
     */
1075 View Code Duplication
    public function loadVersionInfo($contentId, $versionNo)
1076
    {
1077
        $query = $this->queryBuilder->createVersionInfoFindQuery();
1078
        $query->where(
1079
            $query->expr->lAnd(
1080
                $query->expr->eq(
1081
                    $this->dbHandler->quoteColumn('contentobject_id', 'ezcontentobject_version'),
1082
                    $query->bindValue($contentId, null, \PDO::PARAM_INT)
1083
                ),
1084
                $query->expr->eq(
1085
                    $this->dbHandler->quoteColumn('version', 'ezcontentobject_version'),
1086
                    $query->bindValue($versionNo, null, \PDO::PARAM_INT)
1087
                )
1088
            )
1089
        );
1090
        $statement = $query->prepare();
1091
        $statement->execute();
1092
1093
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
1094
    }
1095
1096
    /**
1097
     * Returns data for all versions with given status created by the given $userId.
1098
     *
1099
     * @param int $userId
1100
     * @param int $status
1101
     *
1102
     * @return string[][]
1103
     */
1104
    public function listVersionsForUser($userId, $status = VersionInfo::STATUS_DRAFT)
1105
    {
1106
        $query = $this->queryBuilder->createVersionInfoFindQuery();
1107
        $query->where(
1108
            $query->expr->lAnd(
1109
                $query->expr->eq(
1110
                    $this->dbHandler->quoteColumn('status', 'ezcontentobject_version'),
1111
                    $query->bindValue($status, null, \PDO::PARAM_INT)
1112
                ),
1113
                $query->expr->eq(
1114
                    $this->dbHandler->quoteColumn('creator_id', 'ezcontentobject_version'),
1115
                    $query->bindValue($userId, null, \PDO::PARAM_INT)
1116
                )
1117
            )
1118
        );
1119
1120
        return $this->listVersionsHelper($query);
1121
    }
1122
1123
    /**
1124
     * Returns all version data for the given $contentId, optionally filtered by status.
1125
     *
1126
     * Result is returned with oldest version first (using version id as it has index and is auto increment).
1127
     *
1128
     * @param mixed $contentId
1129
     * @param mixed|null $status Optional argument to filter versions by status, like {@see VersionInfo::STATUS_ARCHIVED}.
1130
     * @param int $limit Limit for items returned, -1 means none.
1131
     *
1132
     * @return string[][]
1133
     */
1134
    public function listVersions($contentId, $status = null, $limit = -1)
1135
    {
1136
        $query = $this->queryBuilder->createVersionInfoFindQuery();
1137
1138
        $filter = $query->expr->eq(
1139
            $this->dbHandler->quoteColumn('contentobject_id', 'ezcontentobject_version'),
1140
            $query->bindValue($contentId, null, \PDO::PARAM_INT)
1141
        );
1142
1143
        if ($status !== null) {
1144
            $filter = $query->expr->lAnd(
1145
                $filter,
1146
                $query->expr->eq(
1147
                    $this->dbHandler->quoteColumn('status', 'ezcontentobject_version'),
1148
                    $query->bindValue($status, null, \PDO::PARAM_INT)
1149
                )
1150
            );
1151
        }
1152
1153
        $query->where($filter);
1154
1155
        if ($limit > 0) {
1156
            $query->limit($limit);
1157
        }
1158
1159
        return $this->listVersionsHelper($query);
1160
    }
1161
1162
    /**
1163
     * Helper for {@see listVersions()} and {@see listVersionsForUser()} that filters duplicates
1164
     * that are the result of the cartesian product performed by createVersionInfoFindQuery().
1165
     *
1166
     * @param \eZ\Publish\Core\Persistence\Database\SelectQuery $query
1167
     *
1168
     * @return string[][]
1169
     */
1170
    private function listVersionsHelper(SelectQuery $query)
1171
    {
1172
        $query->orderBy(
1173
            $this->dbHandler->quoteColumn('id', 'ezcontentobject_version')
1174
        );
1175
1176
        $statement = $query->prepare();
1177
        $statement->execute();
1178
1179
        $results = array();
1180
        $previousId = null;
1181
        foreach ($statement->fetchAll(\PDO::FETCH_ASSOC) as $row) {
1182
            if ($row['ezcontentobject_version_id'] == $previousId) {
1183
                continue;
1184
            }
1185
1186
            $previousId = $row['ezcontentobject_version_id'];
1187
            $results[] = $row;
1188
        }
1189
1190
        return $results;
1191
    }
1192
1193
    /**
1194
     * Returns all version numbers for the given $contentId.
1195
     *
1196
     * @param mixed $contentId
1197
     *
1198
     * @return int[]
1199
     */
1200
    public function listVersionNumbers($contentId)
1201
    {
1202
        $query = $this->dbHandler->createSelectQuery();
1203
        $query->selectDistinct(
1204
            $this->dbHandler->quoteColumn('version')
1205
        )->from(
1206
            $this->dbHandler->quoteTable('ezcontentobject_version')
1207
        )->where(
1208
            $query->expr->eq(
1209
                $this->dbHandler->quoteColumn('contentobject_id'),
1210
                $query->bindValue($contentId, null, \PDO::PARAM_INT)
1211
            )
1212
        );
1213
1214
        $statement = $query->prepare();
1215
        $statement->execute();
1216
1217
        return $statement->fetchAll(\PDO::FETCH_COLUMN);
1218
    }
1219
1220
    /**
1221
     * Returns last version number for content identified by $contentId.
1222
     *
1223
     * @param int $contentId
1224
     *
1225
     * @return int
1226
     */
1227
    public function getLastVersionNumber($contentId)
1228
    {
1229
        $query = $this->dbHandler->createSelectQuery();
1230
        $query->select(
1231
            $query->expr->max($this->dbHandler->quoteColumn('version'))
1232
        )->from(
1233
            $this->dbHandler->quoteTable('ezcontentobject_version')
1234
        )->where(
1235
            $query->expr->eq(
1236
                $this->dbHandler->quoteColumn('contentobject_id'),
1237
                $query->bindValue($contentId, null, \PDO::PARAM_INT)
1238
            )
1239
        );
1240
1241
        $statement = $query->prepare();
1242
        $statement->execute();
1243
1244
        return (int)$statement->fetchColumn();
1245
    }
1246
1247
    /**
1248
     * Returns all IDs for locations that refer to $contentId.
1249
     *
1250
     * @param int $contentId
1251
     *
1252
     * @return int[]
1253
     */
1254
    public function getAllLocationIds($contentId)
1255
    {
1256
        $query = $this->dbHandler->createSelectQuery();
1257
        $query->select(
1258
            $this->dbHandler->quoteColumn('node_id')
1259
        )->from(
1260
            $this->dbHandler->quoteTable('ezcontentobject_tree')
1261
        )->where(
1262
            $query->expr->eq(
1263
                $this->dbHandler->quoteColumn('contentobject_id'),
1264
                $query->bindValue($contentId, null, \PDO::PARAM_INT)
1265
            )
1266
        );
1267
1268
        $statement = $query->prepare();
1269
        $statement->execute();
1270
1271
        return $statement->fetchAll(\PDO::FETCH_COLUMN);
1272
    }
1273
1274
    /**
1275
     * Returns all field IDs of $contentId grouped by their type.
1276
     * If $versionNo is set only field IDs for that version are returned.
1277
     *
1278
     * @param int $contentId
1279
     * @param int|null $versionNo
1280
     *
1281
     * @return int[][]
1282
     */
1283
    public function getFieldIdsByType($contentId, $versionNo = null)
1284
    {
1285
        $query = $this->dbHandler->createSelectQuery();
1286
        $query->select(
1287
            $this->dbHandler->quoteColumn('id'),
1288
            $this->dbHandler->quoteColumn('data_type_string')
1289
        )->from(
1290
            $this->dbHandler->quoteTable('ezcontentobject_attribute')
1291
        )->where(
1292
            $query->expr->eq(
1293
                $this->dbHandler->quoteColumn('contentobject_id'),
1294
                $query->bindValue($contentId, null, \PDO::PARAM_INT)
1295
            )
1296
        );
1297
1298
        if (isset($versionNo)) {
1299
            $query->where(
1300
                $query->expr->eq(
1301
                    $this->dbHandler->quoteColumn('version'),
1302
                    $query->bindValue($versionNo, null, \PDO::PARAM_INT)
1303
                )
1304
            );
1305
        }
1306
1307
        $statement = $query->prepare();
1308
        $statement->execute();
1309
1310
        $result = array();
1311
        foreach ($statement->fetchAll() as $row) {
1312
            if (!isset($result[$row['data_type_string']])) {
1313
                $result[$row['data_type_string']] = array();
1314
            }
1315
            $result[$row['data_type_string']][] = (int)$row['id'];
1316
        }
1317
1318
        return $result;
1319
    }
1320
1321
    /**
1322
     * Deletes relations to and from $contentId.
1323
     * If $versionNo is set only relations for that version are deleted.
1324
     *
1325
     * @param int $contentId
1326
     * @param int|null $versionNo
1327
     */
1328
    public function deleteRelations($contentId, $versionNo = null)
1329
    {
1330
        $query = $this->dbHandler->createDeleteQuery();
1331
        $query->deleteFrom(
1332
            $this->dbHandler->quoteTable('ezcontentobject_link')
1333
        );
1334
1335
        if (isset($versionNo)) {
1336
            $query->where(
1337
                $query->expr->lAnd(
1338
                    $query->expr->eq(
1339
                        $this->dbHandler->quoteColumn('from_contentobject_id'),
1340
                        $query->bindValue($contentId, null, \PDO::PARAM_INT)
1341
                    ),
1342
                    $query->expr->eq(
1343
                        $this->dbHandler->quoteColumn('from_contentobject_version'),
1344
                        $query->bindValue($versionNo, null, \PDO::PARAM_INT)
1345
                    )
1346
                )
1347
            );
1348
        } else {
1349
            $query->where(
1350
                $query->expr->lOr(
1351
                    $query->expr->eq(
1352
                        $this->dbHandler->quoteColumn('from_contentobject_id'),
1353
                        $query->bindValue($contentId, null, \PDO::PARAM_INT)
1354
                    ),
1355
                    $query->expr->eq(
1356
                        $this->dbHandler->quoteColumn('to_contentobject_id'),
1357
                        $query->bindValue($contentId, null, \PDO::PARAM_INT)
1358
                    )
1359
                )
1360
            );
1361
        }
1362
1363
        $query->prepare()->execute();
1364
    }
1365
1366
    /**
1367
     * Removes relations to Content with $contentId from Relation and RelationList field type fields.
1368
     *
1369
     * @param int $contentId
1370
     */
1371
    public function removeReverseFieldRelations($contentId)
1372
    {
1373
        $query = $this->dbHandler->createSelectQuery();
1374
        $query
1375
            ->select('ezcontentobject_attribute.*')
1376
            ->from('ezcontentobject_attribute')
1377
            ->innerJoin(
1378
                'ezcontentobject_link',
1379
                $query->expr->lAnd(
1380
                    $query->expr->eq(
1381
                        $this->dbHandler->quoteColumn('from_contentobject_id', 'ezcontentobject_link'),
1382
                        $this->dbHandler->quoteColumn('contentobject_id', 'ezcontentobject_attribute')
1383
                    ),
1384
                    $query->expr->eq(
1385
                        $this->dbHandler->quoteColumn('from_contentobject_version', 'ezcontentobject_link'),
1386
                        $this->dbHandler->quoteColumn('version', 'ezcontentobject_attribute')
1387
                    ),
1388
                    $query->expr->eq(
1389
                        $this->dbHandler->quoteColumn('contentclassattribute_id', 'ezcontentobject_link'),
1390
                        $this->dbHandler->quoteColumn('contentclassattribute_id', 'ezcontentobject_attribute')
1391
                    )
1392
                )
1393
            )
1394
            ->where(
1395
                $query->expr->eq(
1396
                    $this->dbHandler->quoteColumn('to_contentobject_id', 'ezcontentobject_link'),
1397
                    $query->bindValue($contentId, null, PDO::PARAM_INT)
1398
                ),
1399
                $query->expr->gt(
1400
                    $query->expr->bitAnd(
1401
                        $this->dbHandler->quoteColumn('relation_type', 'ezcontentobject_link'),
1402
                        $query->bindValue(8, null, PDO::PARAM_INT)
1403
                    ),
1404
                    0
1405
                )
1406
            );
1407
1408
        $statement = $query->prepare();
1409
        $statement->execute();
1410
1411
        while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
1412
            if ($row['data_type_string'] === 'ezobjectrelation') {
1413
                $this->removeRelationFromRelationField($row);
1414
            }
1415
1416
            if ($row['data_type_string'] === 'ezobjectrelationlist') {
1417
                $this->removeRelationFromRelationListField($contentId, $row);
1418
            }
1419
        }
1420
    }
1421
1422
    /**
1423
     * Updates field value of RelationList field type identified by given $row data,
1424
     * removing relations toward given $contentId.
1425
     *
1426
     * @param int $contentId
1427
     * @param array $row
1428
     */
1429
    protected function removeRelationFromRelationListField($contentId, array $row)
1430
    {
1431
        $document = new DOMDocument('1.0', 'utf-8');
1432
        $document->loadXML($row['data_text']);
1433
1434
        $xpath = new DOMXPath($document);
1435
        $xpathExpression = "//related-objects/relation-list/relation-item[@contentobject-id='{$contentId}']";
1436
1437
        $relationItems = $xpath->query($xpathExpression);
1438
        foreach ($relationItems as $relationItem) {
1439
            $relationItem->parentNode->removeChild($relationItem);
1440
        }
1441
1442
        $query = $this->dbHandler->createUpdateQuery();
1443
        $query
1444
            ->update('ezcontentobject_attribute')
1445
            ->set(
1446
                'data_text',
1447
                $query->bindValue($document->saveXML(), null, PDO::PARAM_STR)
1448
            )
1449
            ->where(
1450
                $query->expr->lAnd(
1451
                    $query->expr->eq(
1452
                        $this->dbHandler->quoteColumn('id'),
1453
                        $query->bindValue($row['id'], null, PDO::PARAM_INT)
1454
                    ),
1455
                    $query->expr->eq(
1456
                        $this->dbHandler->quoteColumn('version'),
1457
                        $query->bindValue($row['version'], null, PDO::PARAM_INT)
1458
                    )
1459
                )
1460
            );
1461
1462
        $query->prepare()->execute();
1463
    }
1464
1465
    /**
1466
     * Updates field value of Relation field type identified by given $row data,
1467
     * removing relation data.
1468
     *
1469
     * @param array $row
1470
     */
1471
    protected function removeRelationFromRelationField(array $row)
1472
    {
1473
        $query = $this->dbHandler->createUpdateQuery();
1474
        $query
1475
            ->update('ezcontentobject_attribute')
1476
            ->set('data_int', $query->bindValue(null, null, PDO::PARAM_INT))
1477
            ->set('sort_key_int', $query->bindValue(0, null, PDO::PARAM_INT))
1478
            ->where(
1479
                $query->expr->lAnd(
1480
                    $query->expr->eq(
1481
                        $this->dbHandler->quoteColumn('id'),
1482
                        $query->bindValue($row['id'], null, PDO::PARAM_INT)
1483
                    ),
1484
                    $query->expr->eq(
1485
                        $this->dbHandler->quoteColumn('version'),
1486
                        $query->bindValue($row['version'], null, PDO::PARAM_INT)
1487
                    )
1488
                )
1489
            );
1490
1491
        $query->prepare()->execute();
1492
    }
1493
1494
    /**
1495
     * Deletes the field with the given $fieldId.
1496
     *
1497
     * @param int $fieldId
1498
     */
1499
    public function deleteField($fieldId)
1500
    {
1501
        $query = $this->dbHandler->createDeleteQuery();
1502
        $query->deleteFrom(
1503
            $this->dbHandler->quoteTable('ezcontentobject_attribute')
1504
        )->where(
1505
            $query->expr->eq(
1506
                $this->dbHandler->quoteColumn('id'),
1507
                $query->bindValue($fieldId, null, \PDO::PARAM_INT)
1508
            )
1509
        );
1510
1511
        $query->prepare()->execute();
1512
    }
1513
1514
    /**
1515
     * Deletes all fields of $contentId in all versions.
1516
     * If $versionNo is set only fields for that version are deleted.
1517
     *
1518
     * @param int $contentId
1519
     * @param int|null $versionNo
1520
     */
1521
    public function deleteFields($contentId, $versionNo = null)
1522
    {
1523
        $query = $this->dbHandler->createDeleteQuery();
1524
        $query->deleteFrom('ezcontentobject_attribute')
1525
            ->where(
1526
                $query->expr->eq(
1527
                    $this->dbHandler->quoteColumn('contentobject_id'),
1528
                    $query->bindValue($contentId, null, \PDO::PARAM_INT)
1529
                )
1530
            );
1531
1532
        if (isset($versionNo)) {
1533
            $query->where(
1534
                $query->expr->eq(
1535
                    $this->dbHandler->quoteColumn('version'),
1536
                    $query->bindValue($versionNo, null, \PDO::PARAM_INT)
1537
                )
1538
            );
1539
        }
1540
1541
        $query->prepare()->execute();
1542
    }
1543
1544
    /**
1545
     * Deletes all versions of $contentId.
1546
     * If $versionNo is set only that version is deleted.
1547
     *
1548
     * @param int $contentId
1549
     * @param int|null $versionNo
1550
     */
1551
    public function deleteVersions($contentId, $versionNo = null)
1552
    {
1553
        $query = $this->dbHandler->createDeleteQuery();
1554
        $query->deleteFrom('ezcontentobject_version')
1555
            ->where(
1556
                $query->expr->eq(
1557
                    $this->dbHandler->quoteColumn('contentobject_id'),
1558
                    $query->bindValue($contentId, null, \PDO::PARAM_INT)
1559
                )
1560
            );
1561
1562
        if (isset($versionNo)) {
1563
            $query->where(
1564
                $query->expr->eq(
1565
                    $this->dbHandler->quoteColumn('version'),
1566
                    $query->bindValue($versionNo, null, \PDO::PARAM_INT)
1567
                )
1568
            );
1569
        }
1570
1571
        $query->prepare()->execute();
1572
    }
1573
1574
    /**
1575
     * Deletes all names of $contentId.
1576
     * If $versionNo is set only names for that version are deleted.
1577
     *
1578
     * @param int $contentId
1579
     * @param int|null $versionNo
1580
     */
1581
    public function deleteNames($contentId, $versionNo = null)
1582
    {
1583
        $query = $this->dbHandler->createDeleteQuery();
1584
        $query->deleteFrom('ezcontentobject_name')
1585
            ->where(
1586
                $query->expr->eq(
1587
                    $this->dbHandler->quoteColumn('contentobject_id'),
1588
                    $query->bindValue($contentId, null, \PDO::PARAM_INT)
1589
                )
1590
            );
1591
1592
        if (isset($versionNo)) {
1593
            $query->where(
1594
                $query->expr->eq(
1595
                    $this->dbHandler->quoteColumn('content_version'),
1596
                    $query->bindValue($versionNo, null, \PDO::PARAM_INT)
1597
                )
1598
            );
1599
        }
1600
1601
        $query->prepare()->execute();
1602
    }
1603
1604
    /**
1605
     * Sets the name for Content $contentId in version $version to $name in $language.
1606
     *
1607
     * @param int $contentId
1608
     * @param int $version
1609
     * @param string $name
1610
     * @param string $language
1611
     */
1612
    public function setName($contentId, $version, $name, $language)
1613
    {
1614
        $language = $this->languageHandler->loadByLanguageCode($language);
1615
1616
        // Is it an insert or an update ?
1617
        $qSelect = $this->dbHandler->createSelectQuery();
1618
        $qSelect
1619
            ->select(
1620
                $qSelect->alias($qSelect->expr->count('*'), 'count')
1621
            )
1622
            ->from($this->dbHandler->quoteTable('ezcontentobject_name'))
1623
            ->where(
1624
                $qSelect->expr->lAnd(
1625
                    $qSelect->expr->eq($this->dbHandler->quoteColumn('contentobject_id'), $qSelect->bindValue($contentId)),
1626
                    $qSelect->expr->eq($this->dbHandler->quoteColumn('content_version'), $qSelect->bindValue($version)),
1627
                    $qSelect->expr->eq($this->dbHandler->quoteColumn('content_translation'), $qSelect->bindValue($language->languageCode))
1628
                )
1629
            );
1630
        $stmt = $qSelect->prepare();
1631
        $stmt->execute();
1632
        $res = $stmt->fetchAll(\PDO::FETCH_ASSOC);
1633
1634
        $insert = $res[0]['count'] == 0;
1635
        if ($insert) {
1636
            $q = $this->dbHandler->createInsertQuery();
1637
            $q->insertInto($this->dbHandler->quoteTable('ezcontentobject_name'));
1638
        } else {
1639
            $q = $this->dbHandler->createUpdateQuery();
1640
            $q->update($this->dbHandler->quoteTable('ezcontentobject_name'))
1641
                ->where(
1642
                    $q->expr->lAnd(
1643
                        $q->expr->eq($this->dbHandler->quoteColumn('contentobject_id'), $q->bindValue($contentId)),
1644
                        $q->expr->eq($this->dbHandler->quoteColumn('content_version'), $q->bindValue($version)),
1645
                        $q->expr->eq($this->dbHandler->quoteColumn('content_translation'), $q->bindValue($language->languageCode))
1646
                    )
1647
                );
1648
        }
1649
1650
        $q->set(
1651
            $this->dbHandler->quoteColumn('contentobject_id'),
1652
            $q->bindValue($contentId, null, \PDO::PARAM_INT)
1653
        )->set(
1654
            $this->dbHandler->quoteColumn('content_version'),
1655
            $q->bindValue($version, null, \PDO::PARAM_INT)
1656
        )->set(
1657
            $this->dbHandler->quoteColumn('language_id'),
1658
            '(' . $this->getLanguageQuery()->getQuery() . ')'
1659
        )->set(
1660
            $this->dbHandler->quoteColumn('content_translation'),
1661
            $q->bindValue($language->languageCode)
1662
        )->set(
1663
            $this->dbHandler->quoteColumn('real_translation'),
1664
            $q->bindValue($language->languageCode)
1665
        )->set(
1666
            $this->dbHandler->quoteColumn('name'),
1667
            $q->bindValue($name)
1668
        );
1669
        $q->bindValue($language->id, ':languageId', \PDO::PARAM_INT);
1670
        $q->bindValue($contentId, ':contentId', \PDO::PARAM_INT);
1671
        $q->prepare()->execute();
1672
    }
1673
1674
    /**
1675
     * Returns a language sub select query for setName.
1676
     *
1677
     * Return sub select query which gets proper language mask for alwaysAvailable Content.
1678
     *
1679
     * @return \eZ\Publish\Core\Persistence\Database\SelectQuery
1680
     */
1681
    private function getLanguageQuery()
1682
    {
1683
        $languageQuery = $this->dbHandler->createSelectQuery();
1684
        $languageQuery
1685
            ->select(
1686
                $languageQuery->expr->searchedCase(
1687
                    [
1688
                        $languageQuery->expr->lAnd(
1689
                            $languageQuery->expr->eq(
1690
                                $this->dbHandler->quoteColumn('initial_language_id'),
1691
                                ':languageId'
1692
                            ),
1693
                            // wrap bitwise check into another "neq" to provide cross-DBMS compatibility
1694
                            $languageQuery->expr->neq(
1695
                                $languageQuery->expr->bitAnd(
1696
                                    $this->dbHandler->quoteColumn('language_mask'),
1697
                                    ':languageId'
1698
                                ),
1699
                                0
1700
                            )
1701
                        ),
1702
                        $languageQuery->expr->bitOr(
1703
                            ':languageId',
1704
                            1
1705
                        ),
1706
                    ],
1707
                    ':languageId'
1708
                )
1709
            )
1710
            ->from('ezcontentobject')
1711
            ->where(
1712
                $languageQuery->expr->eq(
1713
                    'id',
1714
                    ':contentId'
1715
                )
1716
            );
1717
1718
        return $languageQuery;
1719
    }
1720
1721
    /**
1722
     * Deletes the actual content object referred to by $contentId.
1723
     *
1724
     * @param int $contentId
1725
     */
1726
    public function deleteContent($contentId)
1727
    {
1728
        $query = $this->dbHandler->createDeleteQuery();
1729
        $query->deleteFrom('ezcontentobject')
1730
            ->where(
1731
                $query->expr->eq(
1732
                    $this->dbHandler->quoteColumn('id'),
1733
                    $query->bindValue($contentId, null, \PDO::PARAM_INT)
1734
                )
1735
            );
1736
1737
        $query->prepare()->execute();
1738
    }
1739
1740
    /**
1741
     * Loads relations from $contentId to published content, optionally only from $contentVersionNo.
1742
     *
1743
     * $relationType can also be filtered.
1744
     *
1745
     * @param int $contentId
1746
     * @param int $contentVersionNo
1747
     * @param int $relationType
1748
     *
1749
     * @return string[][] array of relation data
1750
     */
1751
    public function loadRelations($contentId, $contentVersionNo = null, $relationType = null)
1752
    {
1753
        $query = $this->queryBuilder->createRelationFindQuery();
1754
        $query->innerJoin(
1755
            $query->alias(
1756
                $this->dbHandler->quoteTable('ezcontentobject'),
1757
                'ezcontentobject_to'
1758
            ),
1759
            $query->expr->lAnd(
1760
                $query->expr->eq(
1761
                    $this->dbHandler->quoteColumn('to_contentobject_id', 'ezcontentobject_link'),
1762
                    $this->dbHandler->quoteColumn('id', 'ezcontentobject_to')
1763
                ),
1764
                $query->expr->eq(
1765
                    $this->dbHandler->quoteColumn('status', 'ezcontentobject_to'),
1766
                    $query->bindValue(1, null, \PDO::PARAM_INT)
1767
                )
1768
            )
1769
        )->where(
1770
            $query->expr->eq(
1771
                $this->dbHandler->quoteColumn('from_contentobject_id', 'ezcontentobject_link'),
1772
                $query->bindValue($contentId, null, \PDO::PARAM_INT)
1773
            )
1774
        );
1775
1776
        // source version number
1777
        if (isset($contentVersionNo)) {
1778
            $query->where(
1779
                $query->expr->eq(
1780
                    $this->dbHandler->quoteColumn('from_contentobject_version', 'ezcontentobject_link'),
1781
                    $query->bindValue($contentVersionNo, null, \PDO::PARAM_INT)
1782
                )
1783
            );
1784
        } else { // from published version only
1785
            $query->from(
1786
                $this->dbHandler->quoteTable('ezcontentobject')
1787
            )->where(
1788
                $query->expr->lAnd(
1789
                    $query->expr->eq(
1790
                        $this->dbHandler->quoteColumn('id', 'ezcontentobject'),
1791
                        $this->dbHandler->quoteColumn('from_contentobject_id', 'ezcontentobject_link')
1792
                    ),
1793
                    $query->expr->eq(
1794
                        $this->dbHandler->quoteColumn('current_version', 'ezcontentobject'),
1795
                        $this->dbHandler->quoteColumn('from_contentobject_version', 'ezcontentobject_link')
1796
                    )
1797
                )
1798
            );
1799
        }
1800
1801
        // relation type
1802 View Code Duplication
        if (isset($relationType)) {
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...
1803
            $query->where(
1804
                $query->expr->gt(
1805
                    $query->expr->bitAnd(
1806
                        $this->dbHandler->quoteColumn('relation_type', 'ezcontentobject_link'),
1807
                        $query->bindValue($relationType, null, \PDO::PARAM_INT)
1808
                    ),
1809
                    0
1810
                )
1811
            );
1812
        }
1813
1814
        $statement = $query->prepare();
1815
        $statement->execute();
1816
1817
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
1818
    }
1819
1820
    /**
1821
     * Loads data that related to $toContentId.
1822
     *
1823
     * @param int $toContentId
1824
     * @param int $relationType
1825
     *
1826
     * @return mixed[][] Content data, array structured like {@see \eZ\Publish\Core\Persistence\Legacy\Content\Gateway::load()}
1827
     */
1828
    public function loadReverseRelations($toContentId, $relationType = null)
1829
    {
1830
        $query = $this->queryBuilder->createRelationFindQuery();
1831
        $query->where(
1832
            $query->expr->eq(
1833
                $this->dbHandler->quoteColumn('to_contentobject_id', 'ezcontentobject_link'),
1834
                $query->bindValue($toContentId, null, \PDO::PARAM_INT)
1835
            )
1836
        );
1837
1838
        // ezcontentobject join
1839
        $query->from(
1840
            $this->dbHandler->quoteTable('ezcontentobject')
1841
        )->where(
1842
            $query->expr->lAnd(
1843
                $query->expr->eq(
1844
                    $this->dbHandler->quoteColumn('id', 'ezcontentobject'),
1845
                    $this->dbHandler->quoteColumn('from_contentobject_id', 'ezcontentobject_link')
1846
                ),
1847
                $query->expr->eq(
1848
                    $this->dbHandler->quoteColumn('current_version', 'ezcontentobject'),
1849
                    $this->dbHandler->quoteColumn('from_contentobject_version', 'ezcontentobject_link')
1850
                ),
1851
                $query->expr->eq(
1852
                    $this->dbHandler->quoteColumn('status', 'ezcontentobject'),
1853
                    $query->bindValue(1, null, \PDO::PARAM_INT)
1854
                )
1855
            )
1856
        );
1857
1858
        // relation type
1859 View Code Duplication
        if (isset($relationType)) {
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...
1860
            $query->where(
1861
                $query->expr->gt(
1862
                    $query->expr->bitAnd(
1863
                        $this->dbHandler->quoteColumn('relation_type', 'ezcontentobject_link'),
1864
                        $query->bindValue($relationType, null, \PDO::PARAM_INT)
1865
                    ),
1866
                    0
1867
                )
1868
            );
1869
        }
1870
1871
        $statement = $query->prepare();
1872
1873
        $statement->execute();
1874
1875
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
1876
    }
1877
1878
    /**
1879
     * Inserts a new relation database record.
1880
     *
1881
     * @param \eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct $createStruct
1882
     *
1883
     * @return int ID the inserted ID
1884
     */
1885 View Code Duplication
    public function insertRelation(RelationCreateStruct $createStruct)
1886
    {
1887
        $q = $this->dbHandler->createInsertQuery();
1888
        $q->insertInto(
1889
            $this->dbHandler->quoteTable('ezcontentobject_link')
1890
        )->set(
1891
            $this->dbHandler->quoteColumn('id'),
1892
            $this->dbHandler->getAutoIncrementValue('ezcontentobject_link', 'id')
1893
        )->set(
1894
            $this->dbHandler->quoteColumn('contentclassattribute_id'),
1895
            $q->bindValue((int)$createStruct->sourceFieldDefinitionId, null, \PDO::PARAM_INT)
1896
        )->set(
1897
            $this->dbHandler->quoteColumn('from_contentobject_id'),
1898
            $q->bindValue($createStruct->sourceContentId, null, \PDO::PARAM_INT)
1899
        )->set(
1900
            $this->dbHandler->quoteColumn('from_contentobject_version'),
1901
            $q->bindValue($createStruct->sourceContentVersionNo, null, \PDO::PARAM_INT)
1902
        )->set(
1903
            $this->dbHandler->quoteColumn('relation_type'),
1904
            $q->bindValue($createStruct->type, null, \PDO::PARAM_INT)
1905
        )->set(
1906
            $this->dbHandler->quoteColumn('to_contentobject_id'),
1907
            $q->bindValue($createStruct->destinationContentId, null, \PDO::PARAM_INT)
1908
        );
1909
1910
        $q->prepare()->execute();
1911
1912
        return $this->dbHandler->lastInsertId(
1913
            $this->dbHandler->getSequenceName('ezcontentobject_link', 'id')
1914
        );
1915
    }
1916
1917
    /**
1918
     * Deletes the relation with the given $relationId.
1919
     *
1920
     * @param int $relationId
1921
     * @param int $type {@see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON,
1922
     *                 \eZ\Publish\API\Repository\Values\Content\Relation::EMBED,
1923
     *                 \eZ\Publish\API\Repository\Values\Content\Relation::LINK,
1924
     *                 \eZ\Publish\API\Repository\Values\Content\Relation::FIELD}
1925
     */
1926
    public function deleteRelation($relationId, $type)
1927
    {
1928
        // Legacy Storage stores COMMON, LINK and EMBED types using bitmask, therefore first load
1929
        // existing relation type by given $relationId for comparison
1930
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
1931
        $query = $this->dbHandler->createSelectQuery();
1932
        $query->select(
1933
            $this->dbHandler->quoteColumn('relation_type')
1934
        )->from(
1935
            $this->dbHandler->quoteTable('ezcontentobject_link')
1936
        )->where(
1937
            $query->expr->eq(
1938
                $this->dbHandler->quoteColumn('id'),
1939
                $query->bindValue($relationId, null, \PDO::PARAM_INT)
1940
            )
1941
        );
1942
1943
        $statement = $query->prepare();
1944
        $statement->execute();
1945
        $loadedRelationType = $statement->fetchColumn();
1946
1947
        if (!$loadedRelationType) {
1948
            return;
1949
        }
1950
1951
        // If relation type matches then delete
1952
        if ($loadedRelationType == $type) {
1953
            /** @var $query \eZ\Publish\Core\Persistence\Database\DeleteQuery */
1954
            $query = $this->dbHandler->createDeleteQuery();
1955
            $query->deleteFrom(
1956
                'ezcontentobject_link'
1957
            )->where(
1958
                $query->expr->eq(
1959
                    $this->dbHandler->quoteColumn('id'),
1960
                    $query->bindValue($relationId, null, \PDO::PARAM_INT)
1961
                )
1962
            );
1963
1964
            $query->prepare()->execute();
1965
        } elseif ($loadedRelationType & $type) { // If relation type is composite update bitmask
1966
            /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
1967
            $query = $this->dbHandler->createUpdateQuery();
1968
            $query->update(
1969
                $this->dbHandler->quoteTable('ezcontentobject_link')
1970
            )->set(
1971
                $this->dbHandler->quoteColumn('relation_type'),
1972
                $query->expr->bitAnd(
1973
                    $this->dbHandler->quoteColumn('relation_type'),
1974
                    $query->bindValue(~$type, null, \PDO::PARAM_INT)
1975
                )
1976
            )->where(
1977
                $query->expr->eq(
1978
                    $this->dbHandler->quoteColumn('id'),
1979
                    $query->bindValue($relationId, null, \PDO::PARAM_INT)
1980
                )
1981
            );
1982
1983
            $query->prepare()->execute();
1984
        } else {
1985
            // No match, do nothing
1986
        }
1987
    }
1988
1989
    /**
1990
     * Returns all Content IDs for a given $contentTypeId.
1991
     *
1992
     * @param int $contentTypeId
1993
     *
1994
     * @return int[]
1995
     */
1996
    public function getContentIdsByContentTypeId($contentTypeId)
1997
    {
1998
        $query = $this->dbHandler->createSelectQuery();
1999
        $query
2000
            ->select($this->dbHandler->quoteColumn('id'))
2001
            ->from($this->dbHandler->quoteTable('ezcontentobject'))
2002
            ->where(
2003
                $query->expr->eq(
2004
                    $this->dbHandler->quoteColumn('contentclass_id'),
2005
                    $query->bindValue($contentTypeId, null, PDO::PARAM_INT)
2006
                )
2007
            );
2008
2009
        $statement = $query->prepare();
2010
        $statement->execute();
2011
2012
        return $statement->fetchAll(PDO::FETCH_COLUMN);
2013
    }
2014
2015
    /**
2016
     * Load name data for set of content id's and corresponding version number.
2017
     *
2018
     * @param array[] $rows array of hashes with 'id' and 'version' to load names for
2019
     *
2020
     * @return array
2021
     */
2022
    public function loadVersionedNameData($rows)
2023
    {
2024
        $query = $this->queryBuilder->createNamesQuery();
2025
        $conditions = array();
2026
        foreach ($rows as $row) {
2027
            $conditions[] = $query->expr->lAnd(
2028
                $query->expr->eq(
2029
                    $this->dbHandler->quoteColumn('contentobject_id'),
2030
                    $query->bindValue($row['id'], null, \PDO::PARAM_INT)
2031
                ),
2032
                $query->expr->eq(
2033
                    $this->dbHandler->quoteColumn('content_version'),
2034
                    $query->bindValue($row['version'], null, \PDO::PARAM_INT)
2035
                )
2036
            );
2037
        }
2038
2039
        $query->where($query->expr->lOr($conditions));
2040
        $stmt = $query->prepare();
2041
        $stmt->execute();
2042
2043
        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
2044
    }
2045
2046
    /**
2047
     * Batch method for copying all relation meta data for copied Content object.
2048
     *
2049
     * {@inheritdoc}
2050
     *
2051
     * @param int $originalContentId
2052
     * @param int $copiedContentId
2053
     * @param int|null $versionNo If specified only copy for a given version number, otherwise all.
2054
     */
2055
    public function copyRelations($originalContentId, $copiedContentId, $versionNo = null)
2056
    {
2057
        // Given we can retain all columns, we just create copies with new `from_contentobject_id` using INSERT INTO SELECT
2058
        $sql = 'INSERT INTO ezcontentobject_link ( contentclassattribute_id, from_contentobject_id, from_contentobject_version, relation_type, to_contentobject_id )
2059
                SELECT  L2.contentclassattribute_id, :copied_id, L2.from_contentobject_version, L2.relation_type, L2.to_contentobject_id
2060
                FROM    ezcontentobject_link AS L2
2061
                WHERE   L2.from_contentobject_id = :original_id';
2062
2063
        if ($versionNo) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $versionNo of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2064
            $stmt = $this->connection->prepare($sql . ' AND L2.from_contentobject_version = :version');
2065
            $stmt->bindValue('version', $versionNo, PDO::PARAM_INT);
2066
        } else {
2067
            $stmt = $this->connection->prepare($sql);
2068
        }
2069
2070
        $stmt->bindValue('original_id', $originalContentId, PDO::PARAM_INT);
2071
        $stmt->bindValue('copied_id', $copiedContentId, PDO::PARAM_INT);
2072
2073
        $stmt->execute();
2074
    }
2075
}
2076