Completed
Push — 6.7 ( 398a3b...b4417c )
by
unknown
27:30
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 eZ\Publish\Core\Persistence\Legacy\Content\Gateway;
13
use eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder;
14
use eZ\Publish\Core\Persistence\Database\DatabaseHandler;
15
use eZ\Publish\Core\Persistence\Database\UpdateQuery;
16
use eZ\Publish\Core\Persistence\Database\InsertQuery;
17
use eZ\Publish\Core\Persistence\Database\SelectQuery;
18
use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue;
19
use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator;
20
use eZ\Publish\SPI\Persistence\Content;
21
use eZ\Publish\SPI\Persistence\Content\CreateStruct;
22
use eZ\Publish\SPI\Persistence\Content\UpdateStruct;
23
use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct;
24
use eZ\Publish\SPI\Persistence\Content\ContentInfo;
25
use eZ\Publish\SPI\Persistence\Content\VersionInfo;
26
use eZ\Publish\SPI\Persistence\Content\Field;
27
use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct;
28
use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler;
29
use eZ\Publish\Core\Base\Exceptions\NotFoundException as NotFound;
30
use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo;
31
use DOMXPath;
32
use DOMDocument;
33
use PDO;
34
35
/**
36
 * Doctrine database based content gateway.
37
 */
38
class DoctrineDatabase extends Gateway
39
{
40
    /**
41
     * eZ Doctrine database handler.
42
     *
43
     * @var \eZ\Publish\Core\Persistence\Database\DatabaseHandler
44
     */
45
    protected $dbHandler;
46
47
    /**
48
     * The native Doctrine connection.
49
     *
50
     * Meant to be used to transition from eZ/Zeta interface to Doctrine.
51
     *
52
     * @var \Doctrine\DBAL\Connection
53
     */
54
    protected $connection;
55
56
    /**
57
     * Query builder.
58
     *
59
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder
60
     */
61
    protected $queryBuilder;
62
63
    /**
64
     * Caching language handler.
65
     *
66
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler
67
     */
68
    protected $languageHandler;
69
70
    /**
71
     * Language mask generator.
72
     *
73
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator
74
     */
75
    protected $languageMaskGenerator;
76
77
    /**
78
     * Creates a new gateway based on $db.
79
     *
80
     * @param \eZ\Publish\Core\Persistence\Database\DatabaseHandler $db
81
     * @param \Doctrine\DBAL\Connection $connection
82
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder $queryBuilder
83
     * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $languageHandler
84
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator $languageMaskGenerator
85
     */
86
    public function __construct(
87
        DatabaseHandler $db,
88
        Connection $connection,
89
        QueryBuilder $queryBuilder,
90
        LanguageHandler $languageHandler,
91
        LanguageMaskGenerator $languageMaskGenerator
92
    ) {
93
        $this->dbHandler = $db;
94
        $this->connection = $connection;
95
        $this->queryBuilder = $queryBuilder;
96
        $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...
97
        $this->languageMaskGenerator = $languageMaskGenerator;
98
    }
99
100
    /**
101
     * Get context definition for external storage layers.
102
     *
103
     * @return array
104
     */
105
    public function getContext()
106
    {
107
        return array(
108
            'identifier' => 'LegacyStorage',
109
            'connection' => $this->dbHandler,
110
        );
111
    }
112
113
    /**
114
     * Inserts a new content object.
115
     *
116
     * @param \eZ\Publish\SPI\Persistence\Content\CreateStruct $struct
117
     * @param mixed $currentVersionNo
118
     *
119
     * @return int ID
120
     */
121
    public function insertContentObject(CreateStruct $struct, $currentVersionNo = 1)
122
    {
123
        $initialLanguageId = !empty($struct->mainLanguageId) ? $struct->mainLanguageId : $struct->initialLanguageId;
124
        $initialLanguageCode = $this->languageHandler->load($initialLanguageId)->languageCode;
125
126
        if (isset($struct->name[$initialLanguageCode])) {
127
            $name = $struct->name[$initialLanguageCode];
128
        } else {
129
            $name = '';
130
        }
131
132
        $q = $this->dbHandler->createInsertQuery();
133
        $q->insertInto(
134
            $this->dbHandler->quoteTable('ezcontentobject')
135
        )->set(
136
            $this->dbHandler->quoteColumn('id'),
137
            $this->dbHandler->getAutoIncrementValue('ezcontentobject', 'id')
138
        )->set(
139
            $this->dbHandler->quoteColumn('current_version'),
140
            $q->bindValue($currentVersionNo, null, \PDO::PARAM_INT)
141
        )->set(
142
            $this->dbHandler->quoteColumn('name'),
143
            $q->bindValue($name, null, \PDO::PARAM_STR)
144
        )->set(
145
            $this->dbHandler->quoteColumn('contentclass_id'),
146
            $q->bindValue($struct->typeId, null, \PDO::PARAM_INT)
147
        )->set(
148
            $this->dbHandler->quoteColumn('section_id'),
149
            $q->bindValue($struct->sectionId, null, \PDO::PARAM_INT)
150
        )->set(
151
            $this->dbHandler->quoteColumn('owner_id'),
152
            $q->bindValue($struct->ownerId, null, \PDO::PARAM_INT)
153
        )->set(
154
            $this->dbHandler->quoteColumn('initial_language_id'),
155
            $q->bindValue($initialLanguageId, null, \PDO::PARAM_INT)
156
        )->set(
157
            $this->dbHandler->quoteColumn('remote_id'),
158
            $q->bindValue($struct->remoteId, null, \PDO::PARAM_STR)
159
        )->set(
160
            $this->dbHandler->quoteColumn('modified'),
161
            $q->bindValue(0, null, \PDO::PARAM_INT)
162
        )->set(
163
            $this->dbHandler->quoteColumn('published'),
164
            $q->bindValue(0, null, \PDO::PARAM_INT)
165
        )->set(
166
            $this->dbHandler->quoteColumn('status'),
167
            $q->bindValue(ContentInfo::STATUS_DRAFT, null, \PDO::PARAM_INT)
168
        )->set(
169
            $this->dbHandler->quoteColumn('language_mask'),
170
            $q->bindValue(
171
                $this->generateLanguageMask(
172
                    $struct->fields,
173
                    $initialLanguageCode,
174
                    $struct->alwaysAvailable
175
                ),
176
                null,
177
                \PDO::PARAM_INT
178
            )
179
        );
180
181
        $q->prepare()->execute();
182
183
        return $this->dbHandler->lastInsertId(
184
            $this->dbHandler->getSequenceName('ezcontentobject', 'id')
185
        );
186
    }
187
188
    /**
189
     * Generates a language mask for $version.
190
     *
191
     * @param \eZ\Publish\SPI\Persistence\Content\Field[] $fields
192
     * @param string $initialLanguageCode
193
     * @param bool $alwaysAvailable
194
     *
195
     * @return int
196
     */
197
    protected function generateLanguageMask(array $fields, $initialLanguageCode, $alwaysAvailable)
198
    {
199
        $languages = array($initialLanguageCode => true);
200
        foreach ($fields as $field) {
201
            if (isset($languages[$field->languageCode])) {
202
                continue;
203
            }
204
205
            $languages[$field->languageCode] = true;
206
        }
207
208
        if ($alwaysAvailable) {
209
            $languages['always-available'] = true;
210
        }
211
212
        return $this->languageMaskGenerator->generateLanguageMask($languages);
213
    }
214
215
    /**
216
     * Inserts a new version.
217
     *
218
     * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo
219
     * @param \eZ\Publish\SPI\Persistence\Content\Field[] $fields
220
     *
221
     * @return int ID
222
     */
223
    public function insertVersion(VersionInfo $versionInfo, array $fields)
224
    {
225
        /** @var $q \eZ\Publish\Core\Persistence\Database\InsertQuery */
226
        $q = $this->dbHandler->createInsertQuery();
227
        $q->insertInto(
228
            $this->dbHandler->quoteTable('ezcontentobject_version')
229
        )->set(
230
            $this->dbHandler->quoteColumn('id'),
231
            $this->dbHandler->getAutoIncrementValue('ezcontentobject_version', 'id')
232
        )->set(
233
            $this->dbHandler->quoteColumn('version'),
234
            $q->bindValue($versionInfo->versionNo, null, \PDO::PARAM_INT)
235
        )->set(
236
            $this->dbHandler->quoteColumn('modified'),
237
            $q->bindValue($versionInfo->modificationDate, null, \PDO::PARAM_INT)
238
        )->set(
239
            $this->dbHandler->quoteColumn('creator_id'),
240
            $q->bindValue($versionInfo->creatorId, null, \PDO::PARAM_INT)
241
        )->set(
242
            $this->dbHandler->quoteColumn('created'),
243
            $q->bindValue($versionInfo->creationDate, null, \PDO::PARAM_INT)
244
        )->set(
245
            $this->dbHandler->quoteColumn('status'),
246
            $q->bindValue($versionInfo->status, null, \PDO::PARAM_INT)
247
        )->set(
248
            $this->dbHandler->quoteColumn('initial_language_id'),
249
            $q->bindValue(
250
                $this->languageHandler->loadByLanguageCode($versionInfo->initialLanguageCode)->id,
251
                null,
252
                \PDO::PARAM_INT
253
            )
254
        )->set(
255
            $this->dbHandler->quoteColumn('contentobject_id'),
256
            $q->bindValue($versionInfo->contentInfo->id, null, \PDO::PARAM_INT)
257
        )->set(
258
            // As described in field mapping document
259
            $this->dbHandler->quoteColumn('workflow_event_pos'),
260
            $q->bindValue(0, null, \PDO::PARAM_INT)
261
        )->set(
262
            $this->dbHandler->quoteColumn('language_mask'),
263
            $q->bindValue(
264
                $this->generateLanguageMask(
265
                    $fields,
266
                    $versionInfo->initialLanguageCode,
267
                    $versionInfo->contentInfo->alwaysAvailable
268
                ),
269
                null,
270
                \PDO::PARAM_INT
271
            )
272
        );
273
274
        $q->prepare()->execute();
275
276
        return $this->dbHandler->lastInsertId(
277
            $this->dbHandler->getSequenceName('ezcontentobject_version', 'id')
278
        );
279
    }
280
281
    /**
282
     * Updates an existing content identified by $contentId in respect to $struct.
283
     *
284
     * @param int $contentId
285
     * @param \eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct $struct
286
     * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $prePublishVersionInfo Provided on publish
287
     */
288
    public function updateContent($contentId, MetadataUpdateStruct $struct, VersionInfo $prePublishVersionInfo = null)
289
    {
290
        $q = $this->dbHandler->createUpdateQuery();
291
        $q->update($this->dbHandler->quoteTable('ezcontentobject'));
292
293
        if (isset($struct->name)) {
294
            $q->set(
295
                $this->dbHandler->quoteColumn('name'),
296
                $q->bindValue($struct->name, null, \PDO::PARAM_STR)
297
            );
298
        }
299
        if (isset($struct->mainLanguageId)) {
300
            $q->set(
301
                $this->dbHandler->quoteColumn('initial_language_id'),
302
                $q->bindValue($struct->mainLanguageId, null, \PDO::PARAM_INT)
303
            );
304
        }
305
        if (isset($struct->modificationDate)) {
306
            $q->set(
307
                $this->dbHandler->quoteColumn('modified'),
308
                $q->bindValue($struct->modificationDate, null, \PDO::PARAM_INT)
309
            );
310
        }
311
        if (isset($struct->ownerId)) {
312
            $q->set(
313
                $this->dbHandler->quoteColumn('owner_id'),
314
                $q->bindValue($struct->ownerId, null, \PDO::PARAM_INT)
315
            );
316
        }
317
        if (isset($struct->publicationDate)) {
318
            $q->set(
319
                $this->dbHandler->quoteColumn('published'),
320
                $q->bindValue($struct->publicationDate, null, \PDO::PARAM_INT)
321
            );
322
        }
323
        if (isset($struct->remoteId)) {
324
            $q->set(
325
                $this->dbHandler->quoteColumn('remote_id'),
326
                $q->bindValue($struct->remoteId, null, \PDO::PARAM_STR)
327
            );
328
        }
329
        if ($prePublishVersionInfo !== null) {
330
            $mask = 0;
331
332
            if (isset($struct->alwaysAvailable)) {
333
                $mask |= $struct->alwaysAvailable ? 1 : 0;
334
            } else {
335
                $mask |= $prePublishVersionInfo->contentInfo->alwaysAvailable ? 1 : 0;
336
            }
337
338
            foreach ($prePublishVersionInfo->languageIds as $languageId) {
339
                $mask |= $languageId;
340
            }
341
342
            $q->set(
343
                $this->dbHandler->quoteColumn('language_mask'),
344
                $q->bindValue($mask, null, \PDO::PARAM_INT)
345
            );
346
        }
347
        $q->where(
348
            $q->expr->eq(
349
                $this->dbHandler->quoteColumn('id'),
350
                $q->bindValue($contentId, null, \PDO::PARAM_INT)
351
            )
352
        );
353
        $q->prepare()->execute();
354
355
        // Handle alwaysAvailable flag update separately as it's a more complex task and has impact on several tables
356
        if (isset($struct->alwaysAvailable) || isset($struct->mainLanguageId)) {
357
            $this->updateAlwaysAvailableFlag($contentId, $struct->alwaysAvailable);
358
        }
359
    }
360
361
    /**
362
     * Updates version $versionNo for content identified by $contentId, in respect to $struct.
363
     *
364
     * @param int $contentId
365
     * @param int $versionNo
366
     * @param \eZ\Publish\SPI\Persistence\Content\UpdateStruct $struct
367
     */
368
    public function updateVersion($contentId, $versionNo, UpdateStruct $struct)
369
    {
370
        $q = $this->dbHandler->createUpdateQuery();
371
        $q->update(
372
            $this->dbHandler->quoteTable('ezcontentobject_version')
373
        )->set(
374
            $this->dbHandler->quoteColumn('creator_id'),
375
            $q->bindValue($struct->creatorId, null, \PDO::PARAM_INT)
376
        )->set(
377
            $this->dbHandler->quoteColumn('modified'),
378
            $q->bindValue($struct->modificationDate, null, \PDO::PARAM_INT)
379
        )->set(
380
            $this->dbHandler->quoteColumn('initial_language_id'),
381
            $q->bindValue($struct->initialLanguageId, null, \PDO::PARAM_INT)
382
        )->set(
383
            $this->dbHandler->quoteColumn('language_mask'),
384
            $q->expr->bitOr(
385
                $this->dbHandler->quoteColumn('language_mask'),
386
                $q->bindValue(
387
                    $this->generateLanguageMask(
388
                        $struct->fields,
389
                        $this->languageHandler->load($struct->initialLanguageId)->languageCode,
390
                        false
391
                    ),
392
                    null,
393
                    \PDO::PARAM_INT
394
                )
395
            )
396
        )->where(
397
            $q->expr->lAnd(
398
                $q->expr->eq(
399
                    $this->dbHandler->quoteColumn('contentobject_id'),
400
                    $q->bindValue($contentId, null, \PDO::PARAM_INT)
401
                ),
402
                $q->expr->eq(
403
                    $this->dbHandler->quoteColumn('version'),
404
                    $q->bindValue($versionNo, null, \PDO::PARAM_INT)
405
                )
406
            )
407
        );
408
        $q->prepare()->execute();
409
    }
410
411
    /**
412
     * Updates "always available" flag for Content identified by $contentId, in respect to
413
     * Content's current main language and optionally new $alwaysAvailable state.
414
     *
415
     * @param int $contentId
416
     * @param bool|null $alwaysAvailable New "always available" value or null if not defined
417
     */
418
    public function updateAlwaysAvailableFlag($contentId, $alwaysAvailable = null)
419
    {
420
        // We will need to know some info on the current language mask to update the flag
421
        // everywhere needed
422
        $contentInfoRow = $this->loadContentInfo($contentId);
423
        if (!isset($alwaysAvailable)) {
424
            $alwaysAvailable = (bool)$contentInfoRow['language_mask'] & 1;
425
        }
426
427
        /** @var $q \eZ\Publish\Core\Persistence\Database\UpdateQuery */
428
        $q = $this->dbHandler->createUpdateQuery();
429
        $q
430
            ->update($this->dbHandler->quoteTable('ezcontentobject'))
431
            ->set(
432
                $this->dbHandler->quoteColumn('language_mask'),
433
                $alwaysAvailable ?
434
                    $q->expr->bitOr($this->dbHandler->quoteColumn('language_mask'), 1) :
435
                    $q->expr->bitAnd($this->dbHandler->quoteColumn('language_mask'), -2)
436
            )
437
            ->where(
438
                $q->expr->eq(
439
                    $this->dbHandler->quoteColumn('id'),
440
                    $q->bindValue($contentId, null, \PDO::PARAM_INT)
441
                )
442
            );
443
        $q->prepare()->execute();
444
445
        // Now we need to update ezcontentobject_name
446
        /** @var $qName \eZ\Publish\Core\Persistence\Database\UpdateQuery */
447
        $qName = $this->dbHandler->createUpdateQuery();
448
        $qName
449
            ->update($this->dbHandler->quoteTable('ezcontentobject_name'))
450
            ->set(
451
                $this->dbHandler->quoteColumn('language_id'),
452
                $alwaysAvailable ?
453
                    $qName->expr->bitOr($this->dbHandler->quoteColumn('language_id'), 1) :
454
                    $qName->expr->bitAnd($this->dbHandler->quoteColumn('language_id'), -2)
455
            )
456
            ->where(
457
                $qName->expr->lAnd(
458
                    $qName->expr->eq(
459
                        $this->dbHandler->quoteColumn('contentobject_id'),
460
                        $qName->bindValue($contentId, null, \PDO::PARAM_INT)
461
                    ),
462
                    $qName->expr->eq(
463
                        $this->dbHandler->quoteColumn('content_version'),
464
                        $qName->bindValue(
465
                            $contentInfoRow['current_version'],
466
                            null,
467
                            \PDO::PARAM_INT
468
                        )
469
                    )
470
                )
471
            );
472
        $qName->prepare()->execute();
473
474
        // Now update ezcontentobject_attribute for current version
475
        // Create update query that will be reused
476
        /** @var $qAttr \eZ\Publish\Core\Persistence\Database\UpdateQuery */
477
        $qAttr = $this->dbHandler->createUpdateQuery();
478
        $qAttr
479
            ->update($this->dbHandler->quoteTable('ezcontentobject_attribute'))
480
            ->where(
481
                $qAttr->expr->lAnd(
482
                    $qAttr->expr->eq(
483
                        $this->dbHandler->quoteColumn('contentobject_id'),
484
                        $qAttr->bindValue($contentId, null, \PDO::PARAM_INT)
485
                    ),
486
                    $qAttr->expr->eq(
487
                        $this->dbHandler->quoteColumn('version'),
488
                        $qAttr->bindValue(
489
                            $contentInfoRow['current_version'],
490
                            null,
491
                            \PDO::PARAM_INT
492
                        )
493
                    )
494
                )
495
            );
496
497
        // If there is only a single language, update all fields and return
498
        if (!$this->languageMaskGenerator->isLanguageMaskComposite($contentInfoRow['language_mask'])) {
499
            $qAttr->set(
500
                $this->dbHandler->quoteColumn('language_id'),
501
                $alwaysAvailable ?
502
                    $qAttr->expr->bitOr($this->dbHandler->quoteColumn('language_id'), 1) :
503
                    $qAttr->expr->bitAnd($this->dbHandler->quoteColumn('language_id'), -2)
504
            );
505
            $qAttr->prepare()->execute();
506
507
            return;
508
        }
509
510
        // Otherwise:
511
        // 1. Remove always available flag on all fields
512
        $qAttr->set(
513
            $this->dbHandler->quoteColumn('language_id'),
514
            $qAttr->expr->bitAnd($this->dbHandler->quoteColumn('language_id'), -2)
515
        );
516
        $qAttr->prepare()->execute();
517
518
        // 2. If Content is always available set the flag only on fields in main language
519
        if ($alwaysAvailable) {
520
            $qAttr->set(
521
                $this->dbHandler->quoteColumn('language_id'),
522
                $qAttr->expr->bitOr($this->dbHandler->quoteColumn('language_id'), 1)
523
            );
524
            $qAttr->where(
525
                $qAttr->expr->gt(
526
                    $qAttr->expr->bitAnd(
527
                        $this->dbHandler->quoteColumn('language_id'),
528
                        $qAttr->bindValue($contentInfoRow['initial_language_id'], null, PDO::PARAM_INT)
529
                    ),
530
                    $qAttr->bindValue(0, null, PDO::PARAM_INT)
531
                )
532
            );
533
            $qAttr->prepare()->execute();
534
        }
535
    }
536
537
    /**
538
     * Sets the status of the version identified by $contentId and $version to $status.
539
     *
540
     * The $status can be one of STATUS_DRAFT, STATUS_PUBLISHED, STATUS_ARCHIVED
541
     *
542
     * @param int $contentId
543
     * @param int $version
544
     * @param int $status
545
     *
546
     * @return bool
547
     */
548
    public function setStatus($contentId, $version, $status)
549
    {
550
        $q = $this->dbHandler->createUpdateQuery();
551
        $q->update(
552
            $this->dbHandler->quoteTable('ezcontentobject_version')
553
        )->set(
554
            $this->dbHandler->quoteColumn('status'),
555
            $q->bindValue($status, null, \PDO::PARAM_INT)
556
        )->set(
557
            $this->dbHandler->quoteColumn('modified'),
558
            $q->bindValue(time(), null, \PDO::PARAM_INT)
559
        )->where(
560
            $q->expr->lAnd(
561
                $q->expr->eq(
562
                    $this->dbHandler->quoteColumn('contentobject_id'),
563
                    $q->bindValue($contentId, null, \PDO::PARAM_INT)
564
                ),
565
                $q->expr->eq(
566
                    $this->dbHandler->quoteColumn('version'),
567
                    $q->bindValue($version, null, \PDO::PARAM_INT)
568
                )
569
            )
570
        );
571
        $statement = $q->prepare();
572
        $statement->execute();
573
574
        if ((bool)$statement->rowCount() === false) {
575
            return false;
576
        }
577
578
        if ($status !== APIVersionInfo::STATUS_PUBLISHED) {
579
            return true;
580
        }
581
582
        // If the version's status is PUBLISHED, we set the content to published status as well
583
        $q = $this->dbHandler->createUpdateQuery();
584
        $q->update(
585
            $this->dbHandler->quoteTable('ezcontentobject')
586
        )->set(
587
            $this->dbHandler->quoteColumn('status'),
588
            $q->bindValue(ContentInfo::STATUS_PUBLISHED, null, \PDO::PARAM_INT)
589
        )->set(
590
            $this->dbHandler->quoteColumn('current_version'),
591
            $q->bindValue($version, null, \PDO::PARAM_INT)
592
        )->where(
593
            $q->expr->eq(
594
                $this->dbHandler->quoteColumn('id'),
595
                $q->bindValue($contentId, null, \PDO::PARAM_INT)
596
            )
597
        );
598
        $statement = $q->prepare();
599
        $statement->execute();
600
601
        return (bool)$statement->rowCount();
602
    }
603
604
    /**
605
     * Inserts a new field.
606
     *
607
     * Only used when a new field is created (i.e. a new object or a field in a
608
     * new language!). After that, field IDs need to stay the same, only the
609
     * version number changes.
610
     *
611
     * @param \eZ\Publish\SPI\Persistence\Content $content
612
     * @param \eZ\Publish\SPI\Persistence\Content\Field $field
613
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value
614
     *
615
     * @return int ID
616
     */
617
    public function insertNewField(Content $content, Field $field, StorageFieldValue $value)
618
    {
619
        $q = $this->dbHandler->createInsertQuery();
620
621
        $this->setInsertFieldValues($q, $content, $field, $value);
622
623
        // Insert with auto increment ID
624
        $q->set(
625
            $this->dbHandler->quoteColumn('id'),
626
            $this->dbHandler->getAutoIncrementValue('ezcontentobject_attribute', 'id')
627
        );
628
629
        $q->prepare()->execute();
630
631
        return $this->dbHandler->lastInsertId(
632
            $this->dbHandler->getSequenceName('ezcontentobject_attribute', 'id')
633
        );
634
    }
635
636
    /**
637
     * Inserts an existing field.
638
     *
639
     * Used to insert a field with an exsting ID but a new version number.
640
     *
641
     * @param Content $content
642
     * @param Field $field
643
     * @param StorageFieldValue $value
644
     */
645
    public function insertExistingField(Content $content, Field $field, StorageFieldValue $value)
646
    {
647
        $q = $this->dbHandler->createInsertQuery();
648
649
        $this->setInsertFieldValues($q, $content, $field, $value);
650
651
        $q->set(
652
            $this->dbHandler->quoteColumn('id'),
653
            $q->bindValue($field->id, null, \PDO::PARAM_INT)
654
        );
655
656
        $q->prepare()->execute();
657
    }
658
659
    /**
660
     * Sets field (ezcontentobject_attribute) values to the given query.
661
     *
662
     * @param \eZ\Publish\Core\Persistence\Database\InsertQuery $q
663
     * @param Content $content
664
     * @param Field $field
665
     * @param StorageFieldValue $value
666
     */
667
    protected function setInsertFieldValues(InsertQuery $q, Content $content, Field $field, StorageFieldValue $value)
668
    {
669
        $q->insertInto(
670
            $this->dbHandler->quoteTable('ezcontentobject_attribute')
671
        )->set(
672
            $this->dbHandler->quoteColumn('contentobject_id'),
673
            $q->bindValue($content->versionInfo->contentInfo->id, null, \PDO::PARAM_INT)
674
        )->set(
675
            $this->dbHandler->quoteColumn('contentclassattribute_id'),
676
            $q->bindValue($field->fieldDefinitionId, null, \PDO::PARAM_INT)
677
        )->set(
678
            $this->dbHandler->quoteColumn('data_type_string'),
679
            $q->bindValue($field->type)
680
        )->set(
681
            $this->dbHandler->quoteColumn('language_code'),
682
            $q->bindValue($field->languageCode)
683
        )->set(
684
            $this->dbHandler->quoteColumn('version'),
685
            $q->bindValue($field->versionNo, null, \PDO::PARAM_INT)
686
        )->set(
687
            $this->dbHandler->quoteColumn('data_float'),
688
            $q->bindValue($value->dataFloat)
689
        )->set(
690
            $this->dbHandler->quoteColumn('data_int'),
691
            $q->bindValue($value->dataInt, null, \PDO::PARAM_INT)
692
        )->set(
693
            $this->dbHandler->quoteColumn('data_text'),
694
            $q->bindValue($value->dataText)
695
        )->set(
696
            $this->dbHandler->quoteColumn('sort_key_int'),
697
            $q->bindValue($value->sortKeyInt, null, \PDO::PARAM_INT)
698
        )->set(
699
            $this->dbHandler->quoteColumn('sort_key_string'),
700
            $q->bindValue(mb_substr($value->sortKeyString, 0, 255))
701
        )->set(
702
            $this->dbHandler->quoteColumn('language_id'),
703
            $q->bindValue(
704
                $this->languageMaskGenerator->generateLanguageIndicator(
705
                    $field->languageCode,
706
                    $this->isLanguageAlwaysAvailable($content, $field->languageCode)
707
                ),
708
                null,
709
                \PDO::PARAM_INT
710
            )
711
        );
712
    }
713
714
    /**
715
     * Checks if $languageCode is always available in $content.
716
     *
717
     * @param \eZ\Publish\SPI\Persistence\Content $content
718
     * @param string $languageCode
719
     *
720
     * @return bool
721
     */
722
    protected function isLanguageAlwaysAvailable(Content $content, $languageCode)
723
    {
724
        return
725
            $content->versionInfo->contentInfo->alwaysAvailable &&
726
            $content->versionInfo->contentInfo->mainLanguageCode === $languageCode
727
        ;
728
    }
729
730
    /**
731
     * Updates an existing field.
732
     *
733
     * @param Field $field
734
     * @param StorageFieldValue $value
735
     */
736
    public function updateField(Field $field, StorageFieldValue $value)
737
    {
738
        // Note, no need to care for language_id here, since Content->$alwaysAvailable
739
        // cannot change on update
740
        $q = $this->dbHandler->createUpdateQuery();
741
        $this->setFieldUpdateValues($q, $value);
742
        $q->where(
743
            $q->expr->lAnd(
744
                $q->expr->eq(
745
                    $this->dbHandler->quoteColumn('id'),
746
                    $q->bindValue($field->id, null, \PDO::PARAM_INT)
747
                ),
748
                $q->expr->eq(
749
                    $this->dbHandler->quoteColumn('version'),
750
                    $q->bindValue($field->versionNo, null, \PDO::PARAM_INT)
751
                )
752
            )
753
        );
754
        $q->prepare()->execute();
755
    }
756
757
    /**
758
     * Sets update fields for $value on $q.
759
     *
760
     * @param \eZ\Publish\Core\Persistence\Database\UpdateQuery $q
761
     * @param StorageFieldValue $value
762
     */
763
    protected function setFieldUpdateValues(UpdateQuery $q, StorageFieldValue $value)
764
    {
765
        $q->update(
766
            $this->dbHandler->quoteTable('ezcontentobject_attribute')
767
        )->set(
768
            $this->dbHandler->quoteColumn('data_float'),
769
            $q->bindValue($value->dataFloat)
770
        )->set(
771
            $this->dbHandler->quoteColumn('data_int'),
772
            $q->bindValue($value->dataInt, null, \PDO::PARAM_INT)
773
        )->set(
774
            $this->dbHandler->quoteColumn('data_text'),
775
            $q->bindValue($value->dataText)
776
        )->set(
777
            $this->dbHandler->quoteColumn('sort_key_int'),
778
            $q->bindValue($value->sortKeyInt, null, \PDO::PARAM_INT)
779
        )->set(
780
            $this->dbHandler->quoteColumn('sort_key_string'),
781
            $q->bindValue(mb_substr($value->sortKeyString, 0, 255))
782
        );
783
    }
784
785
    /**
786
     * Updates an existing, non-translatable field.
787
     *
788
     * @param \eZ\Publish\SPI\Persistence\Content\Field $field
789
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value
790
     * @param int $contentId
791
     */
792
    public function updateNonTranslatableField(
793
        Field $field,
794
        StorageFieldValue $value,
795
        $contentId
796
    ) {
797
        // Note, no need to care for language_id here, since Content->$alwaysAvailable
798
        // cannot change on update
799
        $q = $this->dbHandler->createUpdateQuery();
800
        $this->setFieldUpdateValues($q, $value);
801
        $q->where(
802
            $q->expr->lAnd(
803
                $q->expr->eq(
804
                    $this->dbHandler->quoteColumn('contentclassattribute_id'),
805
                    $q->bindValue($field->fieldDefinitionId, null, \PDO::PARAM_INT)
806
                ),
807
                $q->expr->eq(
808
                    $this->dbHandler->quoteColumn('contentobject_id'),
809
                    $q->bindValue($contentId, null, \PDO::PARAM_INT)
810
                ),
811
                $q->expr->eq(
812
                    $this->dbHandler->quoteColumn('version'),
813
                    $q->bindValue($field->versionNo, null, \PDO::PARAM_INT)
814
                )
815
            )
816
        );
817
        $q->prepare()->execute();
818
    }
819
820
    /**
821
     * Loads data for a content object.
822
     *
823
     * Returns an array with the relevant data.
824
     *
825
     * @param mixed $contentId
826
     * @param mixed $version
827
     * @param string[] $translations
828
     *
829
     * @return array
830
     */
831 View Code Duplication
    public function load($contentId, $version, array $translations = null)
832
    {
833
        $query = $this->queryBuilder->createFindQuery($translations);
834
        $query->where(
835
            $query->expr->lAnd(
836
                $query->expr->eq(
837
                    $this->dbHandler->quoteColumn('id', 'ezcontentobject'),
838
                    $query->bindValue($contentId)
839
                ),
840
                $query->expr->eq(
841
                    $this->dbHandler->quoteColumn('version', 'ezcontentobject_version'),
842
                    $query->bindValue($version)
843
                )
844
            )
845
        );
846
        $statement = $query->prepare();
847
        $statement->execute();
848
849
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
850
    }
851
852
    /**
853
     * {@inheritdoc}
854
     */
855
    public function loadContentList(array $contentIds, array $translations = null)
856
    {
857
        return $this->internalLoadContent($contentIds, null, $translations);
0 ignored issues
show
Bug introduced by
It seems like $translations defined by parameter $translations on line 855 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...
858
    }
859
860
    /**
861
     * @see loadContentList()
862
     *
863
     * @param array $contentIds
864
     * @param int $version
865
     * @param string[]|null $translations
866
     *
867
     * @return array
868
     */
869
    private function internalLoadContent(array $contentIds, $version = null, array $translations = null)
870
    {
871
        $queryBuilder = $this->connection->createQueryBuilder();
872
        $expr = $queryBuilder->expr();
873
        $queryBuilder
874
            ->select(
875
                'c.id AS ezcontentobject_id',
876
                'c.contentclass_id AS ezcontentobject_contentclass_id',
877
                'c.section_id AS ezcontentobject_section_id',
878
                'c.owner_id AS ezcontentobject_owner_id',
879
                'c.remote_id AS ezcontentobject_remote_id',
880
                'c.current_version AS ezcontentobject_current_version',
881
                'c.initial_language_id AS ezcontentobject_initial_language_id',
882
                'c.modified AS ezcontentobject_modified',
883
                'c.published AS ezcontentobject_published',
884
                'c.status AS ezcontentobject_status',
885
                'c.name AS ezcontentobject_name',
886
                'c.language_mask AS ezcontentobject_language_mask',
887
                'v.id AS ezcontentobject_version_id',
888
                'v.version AS ezcontentobject_version_version',
889
                'v.modified AS ezcontentobject_version_modified',
890
                'v.creator_id AS ezcontentobject_version_creator_id',
891
                'v.created AS ezcontentobject_version_created',
892
                'v.status AS ezcontentobject_version_status',
893
                'v.language_mask AS ezcontentobject_version_language_mask',
894
                'v.initial_language_id AS ezcontentobject_version_initial_language_id',
895
                'a.id AS ezcontentobject_attribute_id',
896
                'a.contentclassattribute_id AS ezcontentobject_attribute_contentclassattribute_id',
897
                'a.data_type_string AS ezcontentobject_attribute_data_type_string',
898
                'a.language_code AS ezcontentobject_attribute_language_code',
899
                'a.language_id AS ezcontentobject_attribute_language_id',
900
                'a.data_float AS ezcontentobject_attribute_data_float',
901
                'a.data_int AS ezcontentobject_attribute_data_int',
902
                'a.data_text AS ezcontentobject_attribute_data_text',
903
                'a.sort_key_int AS ezcontentobject_attribute_sort_key_int',
904
                'a.sort_key_string AS ezcontentobject_attribute_sort_key_string',
905
                't.main_node_id AS ezcontentobject_tree_main_node_id'
906
            )
907
            ->from('ezcontentobject', 'c')
908
            ->innerJoin(
909
                'c',
910
                'ezcontentobject_version',
911
                'v',
912
                $expr->andX(
913
                    $expr->eq('c.id', 'v.contentobject_id'),
914
                    $expr->eq('v.version', $version ?: 'c.current_version')
915
                )
916
            )
917
            ->innerJoin(
918
                'v',
919
                'ezcontentobject_attribute',
920
                'a',
921
                $expr->andX(
922
                    $expr->eq('v.contentobject_id', 'a.contentobject_id'),
923
                    $expr->eq('v.version', 'a.version')
924
                )
925
            )
926
            ->leftJoin(
927
                'c',
928
                'ezcontentobject_tree',
929
                't',
930
                $expr->andX(
931
                    $expr->eq('c.id', 't.contentobject_id'),
932
                    $expr->eq('t.node_id', 't.main_node_id')
933
                )
934
            );
935
936
        $queryBuilder->where(
937
            $expr->in(
938
                'c.id',
939
                $queryBuilder->createNamedParameter($contentIds, Connection::PARAM_INT_ARRAY)
940
            )
941
        );
942
943
        if (!empty($translations)) {
944
            $queryBuilder->andWhere(
945
                $expr->in(
946
                    'a.language_code',
947
                    $queryBuilder->createNamedParameter($translations, Connection::PARAM_STR_ARRAY)
948
                )
949
            );
950
        }
951
952
        return $queryBuilder->execute()->fetchAll(PDO::FETCH_ASSOC);
953
    }
954
955
    /**
956
     * Get query builder to load Content Info data.
957
     *
958
     * @see loadContentInfo(), loadContentInfoByRemoteId(), loadContentInfoList(), loadContentInfoByLocationId()
959
     *
960
     * @return \Doctrine\DBAL\Query\QueryBuilder
961
     */
962
    private function createLoadContentInfoQueryBuilder()
963
    {
964
        $queryBuilder = $this->connection->createQueryBuilder();
965
        $expr = $queryBuilder->expr();
966
        $queryBuilder
967
            ->select('c.*', 't.main_node_id AS ezcontentobject_tree_main_node_id')
968
            ->from('ezcontentobject', 'c')
969
            ->leftJoin(
970
                'c',
971
                'ezcontentobject_tree',
972
                't',
973
                $expr->andX(
974
                    $expr->eq('c.id', 't.contentobject_id'),
975
                    $expr->eq('t.node_id', 't.main_node_id')
976
                )
977
            );
978
979
        return $queryBuilder;
980
    }
981
982
    /**
983
     * Loads info for content identified by $contentId.
984
     * Will basically return a hash containing all field values for ezcontentobject table plus some additional keys:
985
     *  - always_available => Boolean indicating if content's language mask contains alwaysAvailable bit field
986
     *  - main_language_code => Language code for main (initial) language. E.g. "eng-GB".
987
     *
988
     * @param int $contentId
989
     *
990
     * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException
991
     *
992
     * @return array
993
     */
994 View Code Duplication
    public function loadContentInfo($contentId)
995
    {
996
        $queryBuilder = $this->createLoadContentInfoQueryBuilder();
997
        $queryBuilder
998
            ->where('c.id = :id')
999
            ->setParameter('id', $contentId, PDO::PARAM_INT);
1000
1001
        $results = $queryBuilder->execute()->fetchAll(PDO::FETCH_ASSOC);
1002
        if (empty($results)) {
1003
            throw new NotFound('content', "id: $contentId");
1004
        }
1005
1006
        return $results[0];
1007
    }
1008
1009
    public function loadContentInfoList(array $contentIds)
1010
    {
1011
        $queryBuilder = $this->createLoadContentInfoQueryBuilder();
1012
        $queryBuilder
1013
            ->where('c.id IN (:ids)')
1014
            ->setParameter('ids', $contentIds, Connection::PARAM_INT_ARRAY);
1015
1016
        return $queryBuilder->execute()->fetchAll(PDO::FETCH_ASSOC);
1017
    }
1018
1019
    /**
1020
     * Loads info for a content object identified by its remote ID.
1021
     *
1022
     * Returns an array with the relevant data.
1023
     *
1024
     * @param mixed $remoteId
1025
     *
1026
     * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException
1027
     *
1028
     * @return array
1029
     */
1030 View Code Duplication
    public function loadContentInfoByRemoteId($remoteId)
1031
    {
1032
        $queryBuilder = $this->createLoadContentInfoQueryBuilder();
1033
        $queryBuilder
1034
            ->where('c.remote_id = :id')
1035
            ->setParameter('id', $remoteId, PDO::PARAM_STR);
1036
1037
        $results = $queryBuilder->execute()->fetchAll(PDO::FETCH_ASSOC);
1038
        if (empty($results)) {
1039
            throw new NotFound('content', "remote_id: $remoteId");
1040
        }
1041
1042
        return $results[0];
1043
    }
1044
1045
    /**
1046
     * Loads info for a content object identified by its location ID (node ID).
1047
     *
1048
     * Returns an array with the relevant data.
1049
     *
1050
     * @param int $locationId
1051
     *
1052
     * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException
1053
     *
1054
     * @return array
1055
     */
1056 View Code Duplication
    public function loadContentInfoByLocationId($locationId)
1057
    {
1058
        $queryBuilder = $this->createLoadContentInfoQueryBuilder();
1059
        $queryBuilder
1060
            ->where('t.main_node_id = :id')
1061
            ->setParameter('id', $locationId, PDO::PARAM_INT);
1062
1063
        $results = $queryBuilder->execute()->fetchAll(PDO::FETCH_ASSOC);
1064
        if (empty($results)) {
1065
            throw new NotFound('content', "main_node_id: $locationId");
1066
        }
1067
1068
        return $results[0];
1069
    }
1070
1071
    /**
1072
     * Loads version info for content identified by $contentId and $versionNo.
1073
     * Will basically return a hash containing all field values from ezcontentobject_version table plus following keys:
1074
     *  - names => Hash of content object names. Key is the language code, value is the name.
1075
     *  - 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.
1076
     *  - initial_language_code => Language code for initial language in this version.
1077
     *
1078
     * @param int $contentId
1079
     * @param int $versionNo
1080
     *
1081
     * @return array
1082
     */
1083 View Code Duplication
    public function loadVersionInfo($contentId, $versionNo)
1084
    {
1085
        $query = $this->queryBuilder->createVersionInfoFindQuery();
1086
        $query->where(
1087
            $query->expr->lAnd(
1088
                $query->expr->eq(
1089
                    $this->dbHandler->quoteColumn('contentobject_id', 'ezcontentobject_version'),
1090
                    $query->bindValue($contentId, null, \PDO::PARAM_INT)
1091
                ),
1092
                $query->expr->eq(
1093
                    $this->dbHandler->quoteColumn('version', 'ezcontentobject_version'),
1094
                    $query->bindValue($versionNo, null, \PDO::PARAM_INT)
1095
                )
1096
            )
1097
        );
1098
        $statement = $query->prepare();
1099
        $statement->execute();
1100
1101
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
1102
    }
1103
1104
    /**
1105
     * Returns data for all versions with given status created by the given $userId.
1106
     *
1107
     * @param int $userId
1108
     * @param int $status
1109
     *
1110
     * @return string[][]
1111
     */
1112
    public function listVersionsForUser($userId, $status = VersionInfo::STATUS_DRAFT)
1113
    {
1114
        $query = $this->queryBuilder->createVersionInfoFindQuery();
1115
        $query->where(
1116
            $query->expr->lAnd(
1117
                $query->expr->eq(
1118
                    $this->dbHandler->quoteColumn('status', 'ezcontentobject_version'),
1119
                    $query->bindValue($status, null, \PDO::PARAM_INT)
1120
                ),
1121
                $query->expr->eq(
1122
                    $this->dbHandler->quoteColumn('creator_id', 'ezcontentobject_version'),
1123
                    $query->bindValue($userId, null, \PDO::PARAM_INT)
1124
                )
1125
            )
1126
        );
1127
1128
        return $this->listVersionsHelper($query);
1129
    }
1130
1131
    /**
1132
     * Returns all version data for the given $contentId, optionally filtered by status.
1133
     *
1134
     * Result is returned with oldest version first (using version id as it has index and is auto increment).
1135
     *
1136
     * @param mixed $contentId
1137
     * @param mixed|null $status Optional argument to filter versions by status, like {@see VersionInfo::STATUS_ARCHIVED}.
1138
     * @param int $limit Limit for items returned, -1 means none.
1139
     *
1140
     * @return string[][]
1141
     */
1142
    public function listVersions($contentId, $status = null, $limit = -1)
1143
    {
1144
        $query = $this->queryBuilder->createVersionInfoFindQuery();
1145
1146
        $filter = $query->expr->eq(
1147
            $this->dbHandler->quoteColumn('contentobject_id', 'ezcontentobject_version'),
1148
            $query->bindValue($contentId, null, \PDO::PARAM_INT)
1149
        );
1150
1151
        if ($status !== null) {
1152
            $filter = $query->expr->lAnd(
1153
                $filter,
1154
                $query->expr->eq(
1155
                    $this->dbHandler->quoteColumn('status', 'ezcontentobject_version'),
1156
                    $query->bindValue($status, null, \PDO::PARAM_INT)
1157
                )
1158
            );
1159
        }
1160
1161
        $query->where($filter);
1162
1163
        if ($limit > 0) {
1164
            $query->limit($limit);
1165
        }
1166
1167
        return $this->listVersionsHelper($query);
1168
    }
1169
1170
    /**
1171
     * Helper for {@see listVersions()} and {@see listVersionsForUser()} that filters duplicates
1172
     * that are the result of the cartesian product performed by createVersionInfoFindQuery().
1173
     *
1174
     * @param \eZ\Publish\Core\Persistence\Database\SelectQuery $query
1175
     *
1176
     * @return string[][]
1177
     */
1178
    private function listVersionsHelper(SelectQuery $query)
1179
    {
1180
        $query->orderBy(
1181
            $this->dbHandler->quoteColumn('id', 'ezcontentobject_version')
1182
        );
1183
1184
        $statement = $query->prepare();
1185
        $statement->execute();
1186
1187
        $results = array();
1188
        $previousId = null;
1189
        foreach ($statement->fetchAll(\PDO::FETCH_ASSOC) as $row) {
1190
            if ($row['ezcontentobject_version_id'] == $previousId) {
1191
                continue;
1192
            }
1193
1194
            $previousId = $row['ezcontentobject_version_id'];
1195
            $results[] = $row;
1196
        }
1197
1198
        return $results;
1199
    }
1200
1201
    /**
1202
     * Returns all version numbers for the given $contentId.
1203
     *
1204
     * @param mixed $contentId
1205
     *
1206
     * @return int[]
1207
     */
1208
    public function listVersionNumbers($contentId)
1209
    {
1210
        $query = $this->dbHandler->createSelectQuery();
1211
        $query->selectDistinct(
1212
            $this->dbHandler->quoteColumn('version')
1213
        )->from(
1214
            $this->dbHandler->quoteTable('ezcontentobject_version')
1215
        )->where(
1216
            $query->expr->eq(
1217
                $this->dbHandler->quoteColumn('contentobject_id'),
1218
                $query->bindValue($contentId, null, \PDO::PARAM_INT)
1219
            )
1220
        );
1221
1222
        $statement = $query->prepare();
1223
        $statement->execute();
1224
1225
        return $statement->fetchAll(\PDO::FETCH_COLUMN);
1226
    }
1227
1228
    /**
1229
     * Returns last version number for content identified by $contentId.
1230
     *
1231
     * @param int $contentId
1232
     *
1233
     * @return int
1234
     */
1235
    public function getLastVersionNumber($contentId)
1236
    {
1237
        $query = $this->dbHandler->createSelectQuery();
1238
        $query->select(
1239
            $query->expr->max($this->dbHandler->quoteColumn('version'))
1240
        )->from(
1241
            $this->dbHandler->quoteTable('ezcontentobject_version')
1242
        )->where(
1243
            $query->expr->eq(
1244
                $this->dbHandler->quoteColumn('contentobject_id'),
1245
                $query->bindValue($contentId, null, \PDO::PARAM_INT)
1246
            )
1247
        );
1248
1249
        $statement = $query->prepare();
1250
        $statement->execute();
1251
1252
        return (int)$statement->fetchColumn();
1253
    }
1254
1255
    /**
1256
     * Returns all IDs for locations that refer to $contentId.
1257
     *
1258
     * @param int $contentId
1259
     *
1260
     * @return int[]
1261
     */
1262
    public function getAllLocationIds($contentId)
1263
    {
1264
        $query = $this->dbHandler->createSelectQuery();
1265
        $query->select(
1266
            $this->dbHandler->quoteColumn('node_id')
1267
        )->from(
1268
            $this->dbHandler->quoteTable('ezcontentobject_tree')
1269
        )->where(
1270
            $query->expr->eq(
1271
                $this->dbHandler->quoteColumn('contentobject_id'),
1272
                $query->bindValue($contentId, null, \PDO::PARAM_INT)
1273
            )
1274
        );
1275
1276
        $statement = $query->prepare();
1277
        $statement->execute();
1278
1279
        return $statement->fetchAll(\PDO::FETCH_COLUMN);
1280
    }
1281
1282
    /**
1283
     * Returns all field IDs of $contentId grouped by their type.
1284
     * If $versionNo is set only field IDs for that version are returned.
1285
     *
1286
     * @param int $contentId
1287
     * @param int|null $versionNo
1288
     *
1289
     * @return int[][]
1290
     */
1291
    public function getFieldIdsByType($contentId, $versionNo = null)
1292
    {
1293
        $query = $this->dbHandler->createSelectQuery();
1294
        $query->select(
1295
            $this->dbHandler->quoteColumn('id'),
1296
            $this->dbHandler->quoteColumn('data_type_string')
1297
        )->from(
1298
            $this->dbHandler->quoteTable('ezcontentobject_attribute')
1299
        )->where(
1300
            $query->expr->eq(
1301
                $this->dbHandler->quoteColumn('contentobject_id'),
1302
                $query->bindValue($contentId, null, \PDO::PARAM_INT)
1303
            )
1304
        );
1305
1306
        if (isset($versionNo)) {
1307
            $query->where(
1308
                $query->expr->eq(
1309
                    $this->dbHandler->quoteColumn('version'),
1310
                    $query->bindValue($versionNo, null, \PDO::PARAM_INT)
1311
                )
1312
            );
1313
        }
1314
1315
        $statement = $query->prepare();
1316
        $statement->execute();
1317
1318
        $result = array();
1319
        foreach ($statement->fetchAll() as $row) {
1320
            if (!isset($result[$row['data_type_string']])) {
1321
                $result[$row['data_type_string']] = array();
1322
            }
1323
            $result[$row['data_type_string']][] = (int)$row['id'];
1324
        }
1325
1326
        return $result;
1327
    }
1328
1329
    /**
1330
     * Deletes relations to and from $contentId.
1331
     * If $versionNo is set only relations for that version are deleted.
1332
     *
1333
     * @param int $contentId
1334
     * @param int|null $versionNo
1335
     */
1336
    public function deleteRelations($contentId, $versionNo = null)
1337
    {
1338
        $query = $this->dbHandler->createDeleteQuery();
1339
        $query->deleteFrom(
1340
            $this->dbHandler->quoteTable('ezcontentobject_link')
1341
        );
1342
1343
        if (isset($versionNo)) {
1344
            $query->where(
1345
                $query->expr->lAnd(
1346
                    $query->expr->eq(
1347
                        $this->dbHandler->quoteColumn('from_contentobject_id'),
1348
                        $query->bindValue($contentId, null, \PDO::PARAM_INT)
1349
                    ),
1350
                    $query->expr->eq(
1351
                        $this->dbHandler->quoteColumn('from_contentobject_version'),
1352
                        $query->bindValue($versionNo, null, \PDO::PARAM_INT)
1353
                    )
1354
                )
1355
            );
1356
        } else {
1357
            $query->where(
1358
                $query->expr->lOr(
1359
                    $query->expr->eq(
1360
                        $this->dbHandler->quoteColumn('from_contentobject_id'),
1361
                        $query->bindValue($contentId, null, \PDO::PARAM_INT)
1362
                    ),
1363
                    $query->expr->eq(
1364
                        $this->dbHandler->quoteColumn('to_contentobject_id'),
1365
                        $query->bindValue($contentId, null, \PDO::PARAM_INT)
1366
                    )
1367
                )
1368
            );
1369
        }
1370
1371
        $query->prepare()->execute();
1372
    }
1373
1374
    /**
1375
     * Removes relations to Content with $contentId from Relation and RelationList field type fields.
1376
     *
1377
     * @param int $contentId
1378
     */
1379
    public function removeReverseFieldRelations($contentId)
1380
    {
1381
        $query = $this->dbHandler->createSelectQuery();
1382
        $query
1383
            ->select('ezcontentobject_attribute.*')
1384
            ->from('ezcontentobject_attribute')
1385
            ->innerJoin(
1386
                'ezcontentobject_link',
1387
                $query->expr->lAnd(
1388
                    $query->expr->eq(
1389
                        $this->dbHandler->quoteColumn('from_contentobject_id', 'ezcontentobject_link'),
1390
                        $this->dbHandler->quoteColumn('contentobject_id', 'ezcontentobject_attribute')
1391
                    ),
1392
                    $query->expr->eq(
1393
                        $this->dbHandler->quoteColumn('from_contentobject_version', 'ezcontentobject_link'),
1394
                        $this->dbHandler->quoteColumn('version', 'ezcontentobject_attribute')
1395
                    ),
1396
                    $query->expr->eq(
1397
                        $this->dbHandler->quoteColumn('contentclassattribute_id', 'ezcontentobject_link'),
1398
                        $this->dbHandler->quoteColumn('contentclassattribute_id', 'ezcontentobject_attribute')
1399
                    )
1400
                )
1401
            )
1402
            ->where(
1403
                $query->expr->eq(
1404
                    $this->dbHandler->quoteColumn('to_contentobject_id', 'ezcontentobject_link'),
1405
                    $query->bindValue($contentId, null, PDO::PARAM_INT)
1406
                ),
1407
                $query->expr->gt(
1408
                    $query->expr->bitAnd(
1409
                        $this->dbHandler->quoteColumn('relation_type', 'ezcontentobject_link'),
1410
                        $query->bindValue(8, null, PDO::PARAM_INT)
1411
                    ),
1412
                    0
1413
                )
1414
            );
1415
1416
        $statement = $query->prepare();
1417
        $statement->execute();
1418
1419
        while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
1420
            if ($row['data_type_string'] === 'ezobjectrelation') {
1421
                $this->removeRelationFromRelationField($row);
1422
            }
1423
1424
            if ($row['data_type_string'] === 'ezobjectrelationlist') {
1425
                $this->removeRelationFromRelationListField($contentId, $row);
1426
            }
1427
        }
1428
    }
1429
1430
    /**
1431
     * Updates field value of RelationList field type identified by given $row data,
1432
     * removing relations toward given $contentId.
1433
     *
1434
     * @param int $contentId
1435
     * @param array $row
1436
     */
1437
    protected function removeRelationFromRelationListField($contentId, array $row)
1438
    {
1439
        $document = new DOMDocument('1.0', 'utf-8');
1440
        $document->loadXML($row['data_text']);
1441
1442
        $xpath = new DOMXPath($document);
1443
        $xpathExpression = "//related-objects/relation-list/relation-item[@contentobject-id='{$contentId}']";
1444
1445
        $relationItems = $xpath->query($xpathExpression);
1446
        foreach ($relationItems as $relationItem) {
1447
            $relationItem->parentNode->removeChild($relationItem);
1448
        }
1449
1450
        $query = $this->dbHandler->createUpdateQuery();
1451
        $query
1452
            ->update('ezcontentobject_attribute')
1453
            ->set(
1454
                'data_text',
1455
                $query->bindValue($document->saveXML(), null, PDO::PARAM_STR)
1456
            )
1457
            ->where(
1458
                $query->expr->lAnd(
1459
                    $query->expr->eq(
1460
                        $this->dbHandler->quoteColumn('id'),
1461
                        $query->bindValue($row['id'], null, PDO::PARAM_INT)
1462
                    ),
1463
                    $query->expr->eq(
1464
                        $this->dbHandler->quoteColumn('version'),
1465
                        $query->bindValue($row['version'], null, PDO::PARAM_INT)
1466
                    )
1467
                )
1468
            );
1469
1470
        $query->prepare()->execute();
1471
    }
1472
1473
    /**
1474
     * Updates field value of Relation field type identified by given $row data,
1475
     * removing relation data.
1476
     *
1477
     * @param array $row
1478
     */
1479
    protected function removeRelationFromRelationField(array $row)
1480
    {
1481
        $query = $this->dbHandler->createUpdateQuery();
1482
        $query
1483
            ->update('ezcontentobject_attribute')
1484
            ->set('data_int', $query->bindValue(null, null, PDO::PARAM_INT))
1485
            ->set('sort_key_int', $query->bindValue(0, null, PDO::PARAM_INT))
1486
            ->where(
1487
                $query->expr->lAnd(
1488
                    $query->expr->eq(
1489
                        $this->dbHandler->quoteColumn('id'),
1490
                        $query->bindValue($row['id'], null, PDO::PARAM_INT)
1491
                    ),
1492
                    $query->expr->eq(
1493
                        $this->dbHandler->quoteColumn('version'),
1494
                        $query->bindValue($row['version'], null, PDO::PARAM_INT)
1495
                    )
1496
                )
1497
            );
1498
1499
        $query->prepare()->execute();
1500
    }
1501
1502
    /**
1503
     * Deletes the field with the given $fieldId.
1504
     *
1505
     * @param int $fieldId
1506
     */
1507
    public function deleteField($fieldId)
1508
    {
1509
        $query = $this->dbHandler->createDeleteQuery();
1510
        $query->deleteFrom(
1511
            $this->dbHandler->quoteTable('ezcontentobject_attribute')
1512
        )->where(
1513
            $query->expr->eq(
1514
                $this->dbHandler->quoteColumn('id'),
1515
                $query->bindValue($fieldId, null, \PDO::PARAM_INT)
1516
            )
1517
        );
1518
1519
        $query->prepare()->execute();
1520
    }
1521
1522
    /**
1523
     * Deletes all fields of $contentId in all versions.
1524
     * If $versionNo is set only fields for that version are deleted.
1525
     *
1526
     * @param int $contentId
1527
     * @param int|null $versionNo
1528
     */
1529
    public function deleteFields($contentId, $versionNo = null)
1530
    {
1531
        $query = $this->dbHandler->createDeleteQuery();
1532
        $query->deleteFrom('ezcontentobject_attribute')
1533
            ->where(
1534
                $query->expr->eq(
1535
                    $this->dbHandler->quoteColumn('contentobject_id'),
1536
                    $query->bindValue($contentId, null, \PDO::PARAM_INT)
1537
                )
1538
            );
1539
1540
        if (isset($versionNo)) {
1541
            $query->where(
1542
                $query->expr->eq(
1543
                    $this->dbHandler->quoteColumn('version'),
1544
                    $query->bindValue($versionNo, null, \PDO::PARAM_INT)
1545
                )
1546
            );
1547
        }
1548
1549
        $query->prepare()->execute();
1550
    }
1551
1552
    /**
1553
     * Deletes all versions of $contentId.
1554
     * If $versionNo is set only that version is deleted.
1555
     *
1556
     * @param int $contentId
1557
     * @param int|null $versionNo
1558
     */
1559
    public function deleteVersions($contentId, $versionNo = null)
1560
    {
1561
        $query = $this->dbHandler->createDeleteQuery();
1562
        $query->deleteFrom('ezcontentobject_version')
1563
            ->where(
1564
                $query->expr->eq(
1565
                    $this->dbHandler->quoteColumn('contentobject_id'),
1566
                    $query->bindValue($contentId, null, \PDO::PARAM_INT)
1567
                )
1568
            );
1569
1570
        if (isset($versionNo)) {
1571
            $query->where(
1572
                $query->expr->eq(
1573
                    $this->dbHandler->quoteColumn('version'),
1574
                    $query->bindValue($versionNo, null, \PDO::PARAM_INT)
1575
                )
1576
            );
1577
        }
1578
1579
        $query->prepare()->execute();
1580
    }
1581
1582
    /**
1583
     * Deletes all names of $contentId.
1584
     * If $versionNo is set only names for that version are deleted.
1585
     *
1586
     * @param int $contentId
1587
     * @param int|null $versionNo
1588
     */
1589
    public function deleteNames($contentId, $versionNo = null)
1590
    {
1591
        $query = $this->dbHandler->createDeleteQuery();
1592
        $query->deleteFrom('ezcontentobject_name')
1593
            ->where(
1594
                $query->expr->eq(
1595
                    $this->dbHandler->quoteColumn('contentobject_id'),
1596
                    $query->bindValue($contentId, null, \PDO::PARAM_INT)
1597
                )
1598
            );
1599
1600
        if (isset($versionNo)) {
1601
            $query->where(
1602
                $query->expr->eq(
1603
                    $this->dbHandler->quoteColumn('content_version'),
1604
                    $query->bindValue($versionNo, null, \PDO::PARAM_INT)
1605
                )
1606
            );
1607
        }
1608
1609
        $query->prepare()->execute();
1610
    }
1611
1612
    /**
1613
     * Sets the name for Content $contentId in version $version to $name in $language.
1614
     *
1615
     * @param int $contentId
1616
     * @param int $version
1617
     * @param string $name
1618
     * @param string $language
1619
     */
1620
    public function setName($contentId, $version, $name, $language)
1621
    {
1622
        $language = $this->languageHandler->loadByLanguageCode($language);
1623
1624
        // Is it an insert or an update ?
1625
        $qSelect = $this->dbHandler->createSelectQuery();
1626
        $qSelect
1627
            ->select(
1628
                $qSelect->alias($qSelect->expr->count('*'), 'count')
1629
            )
1630
            ->from($this->dbHandler->quoteTable('ezcontentobject_name'))
1631
            ->where(
1632
                $qSelect->expr->lAnd(
1633
                    $qSelect->expr->eq($this->dbHandler->quoteColumn('contentobject_id'), $qSelect->bindValue($contentId)),
1634
                    $qSelect->expr->eq($this->dbHandler->quoteColumn('content_version'), $qSelect->bindValue($version)),
1635
                    $qSelect->expr->eq($this->dbHandler->quoteColumn('content_translation'), $qSelect->bindValue($language->languageCode))
1636
                )
1637
            );
1638
        $stmt = $qSelect->prepare();
1639
        $stmt->execute();
1640
        $res = $stmt->fetchAll(\PDO::FETCH_ASSOC);
1641
1642
        $insert = $res[0]['count'] == 0;
1643
        if ($insert) {
1644
            $q = $this->dbHandler->createInsertQuery();
1645
            $q->insertInto($this->dbHandler->quoteTable('ezcontentobject_name'));
1646
        } else {
1647
            $q = $this->dbHandler->createUpdateQuery();
1648
            $q->update($this->dbHandler->quoteTable('ezcontentobject_name'))
1649
                ->where(
1650
                    $q->expr->lAnd(
1651
                        $q->expr->eq($this->dbHandler->quoteColumn('contentobject_id'), $q->bindValue($contentId)),
1652
                        $q->expr->eq($this->dbHandler->quoteColumn('content_version'), $q->bindValue($version)),
1653
                        $q->expr->eq($this->dbHandler->quoteColumn('content_translation'), $q->bindValue($language->languageCode))
1654
                    )
1655
                );
1656
        }
1657
1658
        $q->set(
1659
            $this->dbHandler->quoteColumn('contentobject_id'),
1660
            $q->bindValue($contentId, null, \PDO::PARAM_INT)
1661
        )->set(
1662
            $this->dbHandler->quoteColumn('content_version'),
1663
            $q->bindValue($version, null, \PDO::PARAM_INT)
1664
        )->set(
1665
            $this->dbHandler->quoteColumn('language_id'),
1666
            '(' . $this->getLanguageQuery()->getQuery() . ')'
1667
        )->set(
1668
            $this->dbHandler->quoteColumn('content_translation'),
1669
            $q->bindValue($language->languageCode)
1670
        )->set(
1671
            $this->dbHandler->quoteColumn('real_translation'),
1672
            $q->bindValue($language->languageCode)
1673
        )->set(
1674
            $this->dbHandler->quoteColumn('name'),
1675
            $q->bindValue($name)
1676
        );
1677
        $q->bindValue($language->id, ':languageId', \PDO::PARAM_INT);
1678
        $q->bindValue($contentId, ':contentId', \PDO::PARAM_INT);
1679
        $q->prepare()->execute();
1680
    }
1681
1682
    /**
1683
     * Returns a language sub select query for setName.
1684
     *
1685
     * Return sub select query which gets proper language mask for alwaysAvailable Content.
1686
     *
1687
     * @return \eZ\Publish\Core\Persistence\Database\SelectQuery
1688
     */
1689
    private function getLanguageQuery()
1690
    {
1691
        $languageQuery = $this->dbHandler->createSelectQuery();
1692
        $languageQuery
1693
            ->select(
1694
                $languageQuery->expr->searchedCase(
1695
                    [
1696
                        $languageQuery->expr->lAnd(
1697
                            $languageQuery->expr->eq(
1698
                                $this->dbHandler->quoteColumn('initial_language_id'),
1699
                                ':languageId'
1700
                            ),
1701
                            // wrap bitwise check into another "neq" to provide cross-DBMS compatibility
1702
                            $languageQuery->expr->neq(
1703
                                $languageQuery->expr->bitAnd(
1704
                                    $this->dbHandler->quoteColumn('language_mask'),
1705
                                    ':languageId'
1706
                                ),
1707
                                0
1708
                            )
1709
                        ),
1710
                        $languageQuery->expr->bitOr(
1711
                            ':languageId',
1712
                            1
1713
                        ),
1714
                    ],
1715
                    ':languageId'
1716
                )
1717
            )
1718
            ->from('ezcontentobject')
1719
            ->where(
1720
                $languageQuery->expr->eq(
1721
                    'id',
1722
                    ':contentId'
1723
                )
1724
            );
1725
1726
        return $languageQuery;
1727
    }
1728
1729
    /**
1730
     * Deletes the actual content object referred to by $contentId.
1731
     *
1732
     * @param int $contentId
1733
     */
1734
    public function deleteContent($contentId)
1735
    {
1736
        $query = $this->dbHandler->createDeleteQuery();
1737
        $query->deleteFrom('ezcontentobject')
1738
            ->where(
1739
                $query->expr->eq(
1740
                    $this->dbHandler->quoteColumn('id'),
1741
                    $query->bindValue($contentId, null, \PDO::PARAM_INT)
1742
                )
1743
            );
1744
1745
        $query->prepare()->execute();
1746
    }
1747
1748
    /**
1749
     * Loads relations from $contentId to published content, optionally only from $contentVersionNo.
1750
     *
1751
     * $relationType can also be filtered.
1752
     *
1753
     * @param int $contentId
1754
     * @param int $contentVersionNo
1755
     * @param int $relationType
1756
     *
1757
     * @return string[][] array of relation data
1758
     */
1759
    public function loadRelations($contentId, $contentVersionNo = null, $relationType = null)
1760
    {
1761
        $query = $this->queryBuilder->createRelationFindQuery();
1762
        $query->innerJoin(
1763
            $query->alias(
1764
                $this->dbHandler->quoteTable('ezcontentobject'),
1765
                'ezcontentobject_to'
1766
            ),
1767
            $query->expr->lAnd(
1768
                $query->expr->eq(
1769
                    $this->dbHandler->quoteColumn('to_contentobject_id', 'ezcontentobject_link'),
1770
                    $this->dbHandler->quoteColumn('id', 'ezcontentobject_to')
1771
                ),
1772
                $query->expr->eq(
1773
                    $this->dbHandler->quoteColumn('status', 'ezcontentobject_to'),
1774
                    $query->bindValue(1, null, \PDO::PARAM_INT)
1775
                )
1776
            )
1777
        )->where(
1778
            $query->expr->eq(
1779
                $this->dbHandler->quoteColumn('from_contentobject_id', 'ezcontentobject_link'),
1780
                $query->bindValue($contentId, null, \PDO::PARAM_INT)
1781
            )
1782
        );
1783
1784
        // source version number
1785
        if (isset($contentVersionNo)) {
1786
            $query->where(
1787
                $query->expr->eq(
1788
                    $this->dbHandler->quoteColumn('from_contentobject_version', 'ezcontentobject_link'),
1789
                    $query->bindValue($contentVersionNo, null, \PDO::PARAM_INT)
1790
                )
1791
            );
1792
        } else { // from published version only
1793
            $query->from(
1794
                $this->dbHandler->quoteTable('ezcontentobject')
1795
            )->where(
1796
                $query->expr->lAnd(
1797
                    $query->expr->eq(
1798
                        $this->dbHandler->quoteColumn('id', 'ezcontentobject'),
1799
                        $this->dbHandler->quoteColumn('from_contentobject_id', 'ezcontentobject_link')
1800
                    ),
1801
                    $query->expr->eq(
1802
                        $this->dbHandler->quoteColumn('current_version', 'ezcontentobject'),
1803
                        $this->dbHandler->quoteColumn('from_contentobject_version', 'ezcontentobject_link')
1804
                    )
1805
                )
1806
            );
1807
        }
1808
1809
        // relation type
1810 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...
1811
            $query->where(
1812
                $query->expr->gt(
1813
                    $query->expr->bitAnd(
1814
                        $this->dbHandler->quoteColumn('relation_type', 'ezcontentobject_link'),
1815
                        $query->bindValue($relationType, null, \PDO::PARAM_INT)
1816
                    ),
1817
                    0
1818
                )
1819
            );
1820
        }
1821
1822
        $statement = $query->prepare();
1823
        $statement->execute();
1824
1825
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
1826
    }
1827
1828
    /**
1829
     * Loads data that related to $toContentId.
1830
     *
1831
     * @param int $toContentId
1832
     * @param int $relationType
1833
     *
1834
     * @return mixed[][] Content data, array structured like {@see \eZ\Publish\Core\Persistence\Legacy\Content\Gateway::load()}
1835
     */
1836
    public function loadReverseRelations($toContentId, $relationType = null)
1837
    {
1838
        $query = $this->queryBuilder->createRelationFindQuery();
1839
        $query->where(
1840
            $query->expr->eq(
1841
                $this->dbHandler->quoteColumn('to_contentobject_id', 'ezcontentobject_link'),
1842
                $query->bindValue($toContentId, null, \PDO::PARAM_INT)
1843
            )
1844
        );
1845
1846
        // ezcontentobject join
1847
        $query->from(
1848
            $this->dbHandler->quoteTable('ezcontentobject')
1849
        )->where(
1850
            $query->expr->lAnd(
1851
                $query->expr->eq(
1852
                    $this->dbHandler->quoteColumn('id', 'ezcontentobject'),
1853
                    $this->dbHandler->quoteColumn('from_contentobject_id', 'ezcontentobject_link')
1854
                ),
1855
                $query->expr->eq(
1856
                    $this->dbHandler->quoteColumn('current_version', 'ezcontentobject'),
1857
                    $this->dbHandler->quoteColumn('from_contentobject_version', 'ezcontentobject_link')
1858
                ),
1859
                $query->expr->eq(
1860
                    $this->dbHandler->quoteColumn('status', 'ezcontentobject'),
1861
                    $query->bindValue(1, null, \PDO::PARAM_INT)
1862
                )
1863
            )
1864
        );
1865
1866
        // relation type
1867 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...
1868
            $query->where(
1869
                $query->expr->gt(
1870
                    $query->expr->bitAnd(
1871
                        $this->dbHandler->quoteColumn('relation_type', 'ezcontentobject_link'),
1872
                        $query->bindValue($relationType, null, \PDO::PARAM_INT)
1873
                    ),
1874
                    0
1875
                )
1876
            );
1877
        }
1878
1879
        $statement = $query->prepare();
1880
1881
        $statement->execute();
1882
1883
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
1884
    }
1885
1886
    /**
1887
     * Inserts a new relation database record.
1888
     *
1889
     * @param \eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct $createStruct
1890
     *
1891
     * @return int ID the inserted ID
1892
     */
1893 View Code Duplication
    public function insertRelation(RelationCreateStruct $createStruct)
1894
    {
1895
        $q = $this->dbHandler->createInsertQuery();
1896
        $q->insertInto(
1897
            $this->dbHandler->quoteTable('ezcontentobject_link')
1898
        )->set(
1899
            $this->dbHandler->quoteColumn('id'),
1900
            $this->dbHandler->getAutoIncrementValue('ezcontentobject_link', 'id')
1901
        )->set(
1902
            $this->dbHandler->quoteColumn('contentclassattribute_id'),
1903
            $q->bindValue((int)$createStruct->sourceFieldDefinitionId, null, \PDO::PARAM_INT)
1904
        )->set(
1905
            $this->dbHandler->quoteColumn('from_contentobject_id'),
1906
            $q->bindValue($createStruct->sourceContentId, null, \PDO::PARAM_INT)
1907
        )->set(
1908
            $this->dbHandler->quoteColumn('from_contentobject_version'),
1909
            $q->bindValue($createStruct->sourceContentVersionNo, null, \PDO::PARAM_INT)
1910
        )->set(
1911
            $this->dbHandler->quoteColumn('relation_type'),
1912
            $q->bindValue($createStruct->type, null, \PDO::PARAM_INT)
1913
        )->set(
1914
            $this->dbHandler->quoteColumn('to_contentobject_id'),
1915
            $q->bindValue($createStruct->destinationContentId, null, \PDO::PARAM_INT)
1916
        );
1917
1918
        $q->prepare()->execute();
1919
1920
        return $this->dbHandler->lastInsertId(
1921
            $this->dbHandler->getSequenceName('ezcontentobject_link', 'id')
1922
        );
1923
    }
1924
1925
    /**
1926
     * Deletes the relation with the given $relationId.
1927
     *
1928
     * @param int $relationId
1929
     * @param int $type {@see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON,
1930
     *                 \eZ\Publish\API\Repository\Values\Content\Relation::EMBED,
1931
     *                 \eZ\Publish\API\Repository\Values\Content\Relation::LINK,
1932
     *                 \eZ\Publish\API\Repository\Values\Content\Relation::FIELD}
1933
     */
1934
    public function deleteRelation($relationId, $type)
1935
    {
1936
        // Legacy Storage stores COMMON, LINK and EMBED types using bitmask, therefore first load
1937
        // existing relation type by given $relationId for comparison
1938
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
1939
        $query = $this->dbHandler->createSelectQuery();
1940
        $query->select(
1941
            $this->dbHandler->quoteColumn('relation_type')
1942
        )->from(
1943
            $this->dbHandler->quoteTable('ezcontentobject_link')
1944
        )->where(
1945
            $query->expr->eq(
1946
                $this->dbHandler->quoteColumn('id'),
1947
                $query->bindValue($relationId, null, \PDO::PARAM_INT)
1948
            )
1949
        );
1950
1951
        $statement = $query->prepare();
1952
        $statement->execute();
1953
        $loadedRelationType = $statement->fetchColumn();
1954
1955
        if (!$loadedRelationType) {
1956
            return;
1957
        }
1958
1959
        // If relation type matches then delete
1960
        if ($loadedRelationType == $type) {
1961
            /** @var $query \eZ\Publish\Core\Persistence\Database\DeleteQuery */
1962
            $query = $this->dbHandler->createDeleteQuery();
1963
            $query->deleteFrom(
1964
                'ezcontentobject_link'
1965
            )->where(
1966
                $query->expr->eq(
1967
                    $this->dbHandler->quoteColumn('id'),
1968
                    $query->bindValue($relationId, null, \PDO::PARAM_INT)
1969
                )
1970
            );
1971
1972
            $query->prepare()->execute();
1973
        } elseif ($loadedRelationType & $type) { // If relation type is composite update bitmask
1974
            /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
1975
            $query = $this->dbHandler->createUpdateQuery();
1976
            $query->update(
1977
                $this->dbHandler->quoteTable('ezcontentobject_link')
1978
            )->set(
1979
                $this->dbHandler->quoteColumn('relation_type'),
1980
                $query->expr->bitAnd(
1981
                    $this->dbHandler->quoteColumn('relation_type'),
1982
                    $query->bindValue(~$type, null, \PDO::PARAM_INT)
1983
                )
1984
            )->where(
1985
                $query->expr->eq(
1986
                    $this->dbHandler->quoteColumn('id'),
1987
                    $query->bindValue($relationId, null, \PDO::PARAM_INT)
1988
                )
1989
            );
1990
1991
            $query->prepare()->execute();
1992
        } else {
1993
            // No match, do nothing
1994
        }
1995
    }
1996
1997
    /**
1998
     * Returns all Content IDs for a given $contentTypeId.
1999
     *
2000
     * @param int $contentTypeId
2001
     *
2002
     * @return int[]
2003
     */
2004
    public function getContentIdsByContentTypeId($contentTypeId)
2005
    {
2006
        $query = $this->dbHandler->createSelectQuery();
2007
        $query
2008
            ->select($this->dbHandler->quoteColumn('id'))
2009
            ->from($this->dbHandler->quoteTable('ezcontentobject'))
2010
            ->where(
2011
                $query->expr->eq(
2012
                    $this->dbHandler->quoteColumn('contentclass_id'),
2013
                    $query->bindValue($contentTypeId, null, PDO::PARAM_INT)
2014
                )
2015
            );
2016
2017
        $statement = $query->prepare();
2018
        $statement->execute();
2019
2020
        return $statement->fetchAll(PDO::FETCH_COLUMN);
2021
    }
2022
2023
    /**
2024
     * Load name data for set of content id's and corresponding version number.
2025
     *
2026
     * @param array[] $rows array of hashes with 'id' and 'version' to load names for
2027
     *
2028
     * @return array
2029
     */
2030
    public function loadVersionedNameData($rows)
2031
    {
2032
        $query = $this->queryBuilder->createNamesQuery();
2033
        $conditions = array();
2034
        foreach ($rows as $row) {
2035
            $conditions[] = $query->expr->lAnd(
2036
                $query->expr->eq(
2037
                    $this->dbHandler->quoteColumn('contentobject_id'),
2038
                    $query->bindValue($row['id'], null, \PDO::PARAM_INT)
2039
                ),
2040
                $query->expr->eq(
2041
                    $this->dbHandler->quoteColumn('content_version'),
2042
                    $query->bindValue($row['version'], null, \PDO::PARAM_INT)
2043
                )
2044
            );
2045
        }
2046
2047
        $query->where($query->expr->lOr($conditions));
2048
        $stmt = $query->prepare();
2049
        $stmt->execute();
2050
2051
        return $stmt->fetchAll(\PDO::FETCH_ASSOC);
2052
    }
2053
2054
    /**
2055
     * Batch method for copying all relation meta data for copied Content object.
2056
     *
2057
     * {@inheritdoc}
2058
     *
2059
     * @param int $originalContentId
2060
     * @param int $copiedContentId
2061
     * @param int|null $versionNo If specified only copy for a given version number, otherwise all.
2062
     */
2063
    public function copyRelations($originalContentId, $copiedContentId, $versionNo = null)
2064
    {
2065
        // Given we can retain all columns, we just create copies with new `from_contentobject_id` using INSERT INTO SELECT
2066
        $sql = 'INSERT INTO ezcontentobject_link ( contentclassattribute_id, from_contentobject_id, from_contentobject_version, relation_type, to_contentobject_id )
2067
                SELECT  L2.contentclassattribute_id, :copied_id, L2.from_contentobject_version, L2.relation_type, L2.to_contentobject_id
2068
                FROM    ezcontentobject_link AS L2
2069
                WHERE   L2.from_contentobject_id = :original_id';
2070
2071
        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...
2072
            $stmt = $this->connection->prepare($sql . ' AND L2.from_contentobject_version = :version');
2073
            $stmt->bindValue('version', $versionNo, PDO::PARAM_INT);
2074
        } else {
2075
            $stmt = $this->connection->prepare($sql);
2076
        }
2077
2078
        $stmt->bindValue('original_id', $originalContentId, PDO::PARAM_INT);
2079
        $stmt->bindValue('copied_id', $copiedContentId, PDO::PARAM_INT);
2080
2081
        $stmt->execute();
2082
    }
2083
}
2084