Completed
Push — 6.7 ( 044712...96c013 )
by André
14:02
created

DoctrineDatabase::getLanguageQuery()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 39
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

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