Completed
Push — ezp-24848_ct_removal_content_i... ( 8854bf...ebe765 )
by
unknown
21:25
created

DoctrineDatabase::isHiddenByParent()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 12
rs 9.2
cc 4
eloc 7
nc 3
nop 2
1
<?php
2
3
/**
4
 * File containing the DoctrineDatabase Location Gateway class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 *
9
 * @version //autogentag//
10
 */
11
namespace eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway;
12
13
use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway;
14
use eZ\Publish\Core\Persistence\Database\DatabaseHandler;
15
use eZ\Publish\Core\Persistence\Database\SelectQuery;
16
use eZ\Publish\Core\Persistence\Database\Query as DatabaseQuery;
17
use eZ\Publish\SPI\Persistence\Content\ContentInfo;
18
use eZ\Publish\SPI\Persistence\Content\Location;
19
use eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct;
20
use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct;
21
use eZ\Publish\API\Repository\Values\Content\Query;
22
use eZ\Publish\API\Repository\Values\Content\Query\SortClause;
23
use eZ\Publish\Core\Base\Exceptions\NotFoundException as NotFound;
24
use RuntimeException;
25
use PDO;
26
27
/**
28
 * Location gateway implementation using the Doctrine database.
29
 */
30
class DoctrineDatabase extends Gateway
31
{
32
    /**
33
     * 2^30, since PHP_INT_MAX can cause overflows in DB systems, if PHP is run
34
     * on 64 bit systems.
35
     */
36
    const MAX_LIMIT = 1073741824;
37
38
    /**
39
     * Database handler.
40
     *
41
     * @var \eZ\Publish\Core\Persistence\Database\DatabaseHandler
42
     */
43
    protected $handler;
44
45
    /**
46
     * Construct from database handler.
47
     *
48
     * @param \eZ\Publish\Core\Persistence\Database\DatabaseHandler $handler
49
     */
50
    public function __construct(DatabaseHandler $handler)
51
    {
52
        $this->handler = $handler;
53
    }
54
55
    /**
56
     * Returns an array with basic node data.
57
     *
58
     * We might want to cache this, since this method is used by about every
59
     * method in the location handler.
60
     *
61
     * @todo optimize
62
     *
63
     * @param mixed $nodeId
64
     *
65
     * @return array
66
     */
67 View Code Duplication
    public function getBasicNodeData($nodeId)
68
    {
69
        $query = $this->handler->createSelectQuery();
70
        $query
71
            ->select('*')
72
            ->from($this->handler->quoteTable('ezcontentobject_tree'))
73
            ->where(
74
                $query->expr->eq(
75
                    $this->handler->quoteColumn('node_id'),
76
                    $query->bindValue($nodeId)
77
                )
78
            );
79
        $statement = $query->prepare();
80
        $statement->execute();
81
82
        if ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
83
            return $row;
84
        }
85
86
        throw new NotFound('location', $nodeId);
87
    }
88
89
    /**
90
     * Returns an array with basic node data.
91
     *
92
     * @todo optimize
93
     *
94
     * @param mixed $remoteId
95
     *
96
     * @return array
97
     */
98 View Code Duplication
    public function getBasicNodeDataByRemoteId($remoteId)
99
    {
100
        $query = $this->handler->createSelectQuery();
101
        $query
102
            ->select('*')
103
            ->from($this->handler->quoteTable('ezcontentobject_tree'))
104
            ->where(
105
                $query->expr->eq(
106
                    $this->handler->quoteColumn('remote_id'),
107
                    $query->bindValue($remoteId)
108
                )
109
            );
110
        $statement = $query->prepare();
111
        $statement->execute();
112
113
        if ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
114
            return $row;
115
        }
116
117
        throw new NotFound('location', $remoteId);
118
    }
119
120
    /**
121
     * Loads data for all Locations for $contentId, optionally only in the
122
     * subtree starting at $rootLocationId.
123
     *
124
     * @param int $contentId
125
     * @param int $rootLocationId
126
     *
127
     * @return array
128
     */
129
    public function loadLocationDataByContent($contentId, $rootLocationId = null)
130
    {
131
        $query = $this->handler->createSelectQuery();
132
        $query
133
            ->select('*')
134
            ->from($this->handler->quoteTable('ezcontentobject_tree'))
135
            ->where(
136
                $query->expr->eq(
137
                    $this->handler->quoteColumn('contentobject_id'),
138
                    $query->bindValue($contentId)
139
                )
140
            );
141
142
        if ($rootLocationId !== null) {
143
            $this->applySubtreeLimitation($query, $rootLocationId);
144
        }
145
146
        $statement = $query->prepare();
147
        $statement->execute();
148
149
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
150
    }
151
152
    /**
153
     * @see \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway::loadParentLocationsDataForDraftContent
154
     */
155
    public function loadParentLocationsDataForDraftContent($contentId, $drafts = null)
156
    {
157
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
158
        $query = $this->handler->createSelectQuery();
159
        $query->selectDistinct(
160
            'ezcontentobject_tree.*'
161
        )->from(
162
            $this->handler->quoteTable('ezcontentobject_tree')
163
        )->innerJoin(
164
            $this->handler->quoteTable('eznode_assignment'),
165
            $query->expr->lAnd(
166
167
                $query->expr->eq(
168
                    $this->handler->quoteColumn('node_id', 'ezcontentobject_tree'),
169
                    $this->handler->quoteColumn('parent_node', 'eznode_assignment')
170
                ),
171
                $query->expr->eq(
172
                    $this->handler->quoteColumn('contentobject_id', 'eznode_assignment'),
173
                    $query->bindValue($contentId, null, \PDO::PARAM_INT)
174
                ),
175
                $query->expr->eq(
176
                    $this->handler->quoteColumn('op_code', 'eznode_assignment'),
177
                    $query->bindValue(self::NODE_ASSIGNMENT_OP_CODE_CREATE, null, \PDO::PARAM_INT)
178
                )
179
            )
180
        )->innerJoin(
181
            $this->handler->quoteTable('ezcontentobject'),
182
            $query->expr->lAnd(
183
                $query->expr->lOr(
184
                    $query->expr->eq(
185
                        $this->handler->quoteColumn('contentobject_id', 'eznode_assignment'),
186
                        $this->handler->quoteColumn('id', 'ezcontentobject')
187
                    )
188
                ),
189
                $query->expr->eq(
190
                    $this->handler->quoteColumn('status', 'ezcontentobject'),
191
                    $query->bindValue(ContentInfo::STATUS_DRAFT, null, \PDO::PARAM_INT)
192
                )
193
            )
194
        );
195
196
        $statement = $query->prepare();
197
        $statement->execute();
198
199
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
200
    }
201
202
    /**
203
     * Find all content in the given subtree.
204
     *
205
     * @param mixed $sourceId
206
     * @param bool $onlyIds
207
     *
208
     * @return array
209
     */
210
    public function getSubtreeContent($sourceId, $onlyIds = false)
211
    {
212
        $query = $this->handler->createSelectQuery();
213
        $query->select($onlyIds ? 'node_id, contentobject_id, depth' : '*')->from(
214
            $this->handler->quoteTable('ezcontentobject_tree')
215
        );
216
        $this->applySubtreeLimitation($query, $sourceId);
217
        $query->orderBy(
218
            $this->handler->quoteColumn('depth', 'ezcontentobject_tree')
219
        )->orderBy(
220
            $this->handler->quoteColumn('node_id', 'ezcontentobject_tree')
221
        );
222
        $statement = $query->prepare();
223
        $statement->execute();
224
225
        $results = $statement->fetchAll($onlyIds ? (PDO::FETCH_COLUMN | PDO::FETCH_GROUP) : PDO::FETCH_ASSOC);
226
        // array_map() is used to to map all elements stored as $results[$i][0] to $results[$i]
227
        return $onlyIds ? array_map('reset', $results) : $results;
228
    }
229
230
    /**
231
     * Limits the given $query to the subtree starting at $rootLocationId.
232
     *
233
     * @param \eZ\Publish\Core\Persistence\Database\Query $query
234
     * @param string $rootLocationId
235
     */
236
    protected function applySubtreeLimitation(DatabaseQuery $query, $rootLocationId)
237
    {
238
        $query->where(
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface eZ\Publish\Core\Persistence\Database\Query as the method where() does only exist in the following implementations of said interface: eZ\Publish\Core\Persiste...ine\DeleteDoctrineQuery, eZ\Publish\Core\Persiste...ine\SelectDoctrineQuery, eZ\Publish\Core\Persiste...\SubselectDoctrineQuery, eZ\Publish\Core\Persiste...ine\UpdateDoctrineQuery.

Let’s take a look at an example:

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

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

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

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

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

Available Fixes

  1. Change the type-hint for the parameter:

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

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

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
239
            $query->expr->like(
240
                $this->handler->quoteColumn('path_string', 'ezcontentobject_tree'),
241
                $query->bindValue('%/' . $rootLocationId . '/%')
242
            )
243
        );
244
    }
245
246
    /**
247
     * Returns data for the first level children of the location identified by given $locationId.
248
     *
249
     * @param mixed $locationId
250
     *
251
     * @return array
252
     */
253
    public function getChildren($locationId)
254
    {
255
        $query = $this->handler->createSelectQuery();
256
        $query->select('*')->from(
257
            $this->handler->quoteTable('ezcontentobject_tree')
258
        )->where(
259
            $query->expr->eq(
260
                $this->handler->quoteColumn('parent_node_id', 'ezcontentobject_tree'),
261
                $query->bindValue($locationId, null, \PDO::PARAM_INT)
262
            )
263
        );
264
        $statement = $query->prepare();
265
        $statement->execute();
266
267
        return $statement->fetchAll(\PDO::FETCH_ASSOC);
268
    }
269
270
    /**
271
     * Update path strings to move nodes in the ezcontentobject_tree table.
272
     *
273
     * This query can likely be optimized to use some more advanced string
274
     * operations, which then depend on the respective database.
275
     *
276
     * @todo optimize
277
     *
278
     * @param array $sourceNodeData
279
     * @param array $destinationNodeData
280
     */
281
    public function moveSubtreeNodes(array $sourceNodeData, array $destinationNodeData)
282
    {
283
        $fromPathString = $sourceNodeData['path_string'];
284
285
        /** @var $query \eZ\Publish\Core\Persistence\Database\SelectQuery */
286
        $query = $this->handler->createSelectQuery();
287
        $query
288
            ->select(
289
                $this->handler->quoteColumn('node_id'),
290
                $this->handler->quoteColumn('parent_node_id'),
291
                $this->handler->quoteColumn('path_string'),
292
                $this->handler->quoteColumn('path_identification_string')
293
            )
294
            ->from($this->handler->quoteTable('ezcontentobject_tree'))
295
            ->where(
296
                $query->expr->like(
297
                    $this->handler->quoteColumn('path_string'),
298
                    $query->bindValue($fromPathString . '%')
299
                )
300
            );
301
        $statement = $query->prepare();
302
        $statement->execute();
303
304
        $rows = $statement->fetchAll();
305
        $oldParentPathString = implode('/', array_slice(explode('/', $fromPathString), 0, -2)) . '/';
306
        $oldParentPathIdentificationString = implode(
307
            '/',
308
            array_slice(explode('/', $sourceNodeData['path_identification_string']), 0, -1)
309
        );
310
311
        foreach ($rows as $row) {
312
            // Prefixing ensures correct replacement when old parent is root node
313
            $newPathString = str_replace(
314
                'prefix' . $oldParentPathString,
315
                $destinationNodeData['path_string'],
316
                'prefix' . $row['path_string']
317
            );
318
            $newPathIdentificationString = str_replace(
319
                'prefix' . $oldParentPathIdentificationString,
320
                $destinationNodeData['path_identification_string'] . '/',
321
                'prefix' . $row['path_identification_string']
322
            );
323
324
            $newParentId = $row['parent_node_id'];
325
            if ($row['path_string'] === $fromPathString) {
326
                $newParentId = (int)implode('', array_slice(explode('/', $newPathString), -3, 1));
327
            }
328
329
            /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
330
            $query = $this->handler->createUpdateQuery();
331
            $query
332
                ->update($this->handler->quoteTable('ezcontentobject_tree'))
333
                ->set(
334
                    $this->handler->quoteColumn('path_string'),
335
                    $query->bindValue($newPathString)
336
                )
337
                ->set(
338
                    $this->handler->quoteColumn('path_identification_string'),
339
                    $query->bindValue($newPathIdentificationString)
340
                )
341
                ->set(
342
                    $this->handler->quoteColumn('depth'),
343
                    $query->bindValue(substr_count($newPathString, '/') - 2)
344
                )
345
                ->set(
346
                    $this->handler->quoteColumn('parent_node_id'),
347
                    $query->bindValue($newParentId)
348
                );
349
350
            if ($destinationNodeData['is_hidden'] || $destinationNodeData['is_invisible']) {
351
                // CASE 1: Mark whole tree as invisible if destination is invisible and/or hidden
352
                $query->set(
353
                    $this->handler->quoteColumn('is_invisible'),
354
                    $query->bindValue(1)
355
                );
356
            } elseif (!$sourceNodeData['is_hidden'] && $sourceNodeData['is_invisible']) {
357
                // CASE 2: source is only invisible, we will need to re-calculate whole moved tree visibility
358
                $query->set(
359
                    $this->handler->quoteColumn('is_invisible'),
360
                    $query->bindValue($this->isHiddenByParent($newPathString, $rows) ? 1 : 0)
361
                );
362
            } else {
363
                // CASE 3: keep invisible flags as is (source is either hidden or not hidden/invisible at all)
364
            }
365
366
            $query->where(
367
                    $query->expr->eq(
368
                        $this->handler->quoteColumn('node_id'),
369
                        $query->bindValue($row['node_id'])
370
                    )
371
                );
372
            $query->prepare()->execute();
373
        }
374
    }
375
376
    private function isHiddenByParent($pathString, array $rows)
377
    {
378
        $parentNodeIds = explode('/', trim($pathString, '/'));
379
        array_pop($parentNodeIds);// remove self
380
        foreach ($rows as $row) {
381
            if ($row['is_hidden'] &&  in_array($row['node_id'], $parentNodeIds)) {
382
                return true;
383
            }
384
        }
385
386
        return false;
387
    }
388
389
    /**
390
     * Updated subtree modification time for all nodes on path.
391
     *
392
     * @param string $pathString
393
     * @param int|null $timestamp
394
     */
395
    public function updateSubtreeModificationTime($pathString, $timestamp = null)
396
    {
397
        $nodes = array_filter(explode('/', $pathString));
398
        $query = $this->handler->createUpdateQuery();
399
        $query
400
            ->update($this->handler->quoteTable('ezcontentobject_tree'))
401
            ->set(
402
                $this->handler->quoteColumn('modified_subnode'),
403
                $query->bindValue(
404
                    $timestamp ?: time()
405
                )
406
            )
407
            ->where(
408
                $query->expr->in(
409
                    $this->handler->quoteColumn('node_id'),
410
                    $nodes
411
                )
412
            );
413
        $query->prepare()->execute();
414
    }
415
416
    /**
417
     * Sets a location to be hidden, and it self + all children to invisible.
418
     *
419
     * @param string $pathString
420
     */
421
    public function hideSubtree($pathString)
422
    {
423
        $query = $this->handler->createUpdateQuery();
424
        $query
425
            ->update($this->handler->quoteTable('ezcontentobject_tree'))
426
            ->set(
427
                $this->handler->quoteColumn('is_invisible'),
428
                $query->bindValue(1)
429
            )
430
            ->set(
431
                $this->handler->quoteColumn('modified_subnode'),
432
                $query->bindValue(time())
433
            )
434
            ->where(
435
                $query->expr->like(
436
                    $this->handler->quoteColumn('path_string'),
437
                    $query->bindValue($pathString . '%')
438
                )
439
            );
440
        $query->prepare()->execute();
441
442
        $query = $this->handler->createUpdateQuery();
443
        $query
444
            ->update($this->handler->quoteTable('ezcontentobject_tree'))
445
            ->set(
446
                $this->handler->quoteColumn('is_hidden'),
447
                $query->bindValue(1)
448
            )
449
            ->where(
450
                $query->expr->eq(
451
                    $this->handler->quoteColumn('path_string'),
452
                    $query->bindValue($pathString)
453
                )
454
            );
455
        $query->prepare()->execute();
456
    }
457
458
    /**
459
     * Sets a location to be unhidden, and self + children to visible unless a parent is hiding the tree.
460
     * If not make sure only children down to first hidden node is marked visible.
461
     *
462
     * @param string $pathString
463
     */
464
    public function unHideSubtree($pathString)
465
    {
466
        // Unhide the requested node
467
        $query = $this->handler->createUpdateQuery();
468
        $query
469
            ->update($this->handler->quoteTable('ezcontentobject_tree'))
470
            ->set(
471
                $this->handler->quoteColumn('is_hidden'),
472
                $query->bindValue(0)
473
            )
474
            ->where(
475
                $query->expr->eq(
476
                    $this->handler->quoteColumn('path_string'),
477
                    $query->bindValue($pathString)
478
                )
479
            );
480
        $query->prepare()->execute();
481
482
        // Check if any parent nodes are explicitly hidden
483
        $query = $this->handler->createSelectQuery();
484
        $query
485
            ->select($this->handler->quoteColumn('path_string'))
486
            ->from($this->handler->quoteTable('ezcontentobject_tree'))
487
            ->where(
488
                $query->expr->lAnd(
489
                    $query->expr->eq(
490
                        $this->handler->quoteColumn('is_hidden'),
491
                        $query->bindValue(1)
492
                    ),
493
                    $query->expr->in(
494
                        $this->handler->quoteColumn('node_id'),
495
                        array_filter(explode('/', $pathString))
496
                    )
497
                )
498
            );
499
        $statement = $query->prepare();
500
        $statement->execute();
501
        if (count($statement->fetchAll(\PDO::FETCH_COLUMN))) {
502
            // There are parent nodes set hidden, so that we can skip marking
503
            // something visible again.
504
            return;
505
        }
506
507
        // Find nodes of explicitly hidden subtrees in the subtree which
508
        // should be unhidden
509
        $query = $this->handler->createSelectQuery();
510
        $query
511
            ->select($this->handler->quoteColumn('path_string'))
512
            ->from($this->handler->quoteTable('ezcontentobject_tree'))
513
            ->where(
514
                $query->expr->lAnd(
515
                    $query->expr->eq(
516
                        $this->handler->quoteColumn('is_hidden'),
517
                        $query->bindValue(1)
518
                    ),
519
                    $query->expr->like(
520
                        $this->handler->quoteColumn('path_string'),
521
                        $query->bindValue($pathString . '%')
522
                    )
523
                )
524
            );
525
        $statement = $query->prepare();
526
        $statement->execute();
527
        $hiddenSubtrees = $statement->fetchAll(\PDO::FETCH_COLUMN);
528
529
        $query = $this->handler->createUpdateQuery();
530
        $query
531
            ->update($this->handler->quoteTable('ezcontentobject_tree'))
532
            ->set(
533
                $this->handler->quoteColumn('is_invisible'),
534
                $query->bindValue(0)
535
            )
536
            ->set(
537
                $this->handler->quoteColumn('modified_subnode'),
538
                $query->bindValue(time())
539
            );
540
541
        // Build where expression selecting the nodes, which should be made
542
        // visible again
543
        $where = $query->expr->like(
544
            $this->handler->quoteColumn('path_string'),
545
            $query->bindValue($pathString . '%')
546
        );
547
        if (count($hiddenSubtrees)) {
548
            $handler = $this->handler;
549
            $where = $query->expr->lAnd(
550
                $where,
551
                $query->expr->lAnd(
552
                    array_map(
553
                        function ($pathString) use ($query, $handler) {
554
                            return $query->expr->not(
555
                                $query->expr->like(
556
                                    $handler->quoteColumn('path_string'),
557
                                    $query->bindValue($pathString . '%')
558
                                )
559
                            );
560
                        },
561
                        $hiddenSubtrees
562
                    )
563
                )
564
            );
565
        }
566
        $query->where($where);
567
        $statement = $query->prepare()->execute();
0 ignored issues
show
Unused Code introduced by
$statement is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
568
    }
569
570
    /**
571
     * Swaps the content object being pointed to by a location object.
572
     *
573
     * Make the location identified by $locationId1 refer to the Content
574
     * referred to by $locationId2 and vice versa.
575
     *
576
     * @param mixed $locationId1
577
     * @param mixed $locationId2
578
     *
579
     * @return bool
580
     */
581
    public function swap($locationId1, $locationId2)
582
    {
583
        $query = $this->handler->createSelectQuery();
584
        $query
585
            ->select(
586
                $this->handler->quoteColumn('node_id'),
587
                $this->handler->quoteColumn('contentobject_id'),
588
                $this->handler->quoteColumn('contentobject_version')
589
            )
590
            ->from($this->handler->quoteTable('ezcontentobject_tree'))
591
            ->where(
592
                $query->expr->in(
593
                    $this->handler->quoteColumn('node_id'),
594
                    array($locationId1, $locationId2)
595
                )
596
            );
597
        $statement = $query->prepare();
598
        $statement->execute();
599
        foreach ($statement->fetchAll() as $row) {
600
            $contentObjects[$row['node_id']] = $row;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$contentObjects was never initialized. Although not strictly required by PHP, it is generally a good practice to add $contentObjects = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
601
        }
602
603
        $query = $this->handler->createUpdateQuery();
604
        $query
605
            ->update($this->handler->quoteTable('ezcontentobject_tree'))
606
            ->set(
607
                $this->handler->quoteColumn('contentobject_id'),
608
                $query->bindValue($contentObjects[$locationId2]['contentobject_id'])
0 ignored issues
show
Bug introduced by
The variable $contentObjects does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
609
            )
610
            ->set(
611
                $this->handler->quoteColumn('contentobject_version'),
612
                $query->bindValue($contentObjects[$locationId2]['contentobject_version'])
613
            )
614
            ->where(
615
                $query->expr->eq(
616
                    $this->handler->quoteColumn('node_id'),
617
                    $query->bindValue($locationId1)
618
                )
619
            );
620
        $query->prepare()->execute();
621
622
        $query = $this->handler->createUpdateQuery();
623
        $query
624
            ->update($this->handler->quoteTable('ezcontentobject_tree'))
625
            ->set(
626
                $this->handler->quoteColumn('contentobject_id'),
627
                $query->bindValue($contentObjects[$locationId1]['contentobject_id'])
628
            )
629
            ->set(
630
                $this->handler->quoteColumn('contentobject_version'),
631
                $query->bindValue($contentObjects[$locationId1]['contentobject_version'])
632
            )
633
            ->where(
634
                $query->expr->eq(
635
                    $this->handler->quoteColumn('node_id'),
636
                    $query->bindValue($locationId2)
637
                )
638
            );
639
        $query->prepare()->execute();
640
    }
641
642
    /**
643
     * Creates a new location in given $parentNode.
644
     *
645
     * @param \eZ\Publish\SPI\Persistence\Content\Location\CreateStruct $createStruct
646
     * @param array $parentNode
647
     *
648
     * @return \eZ\Publish\SPI\Persistence\Content\Location
649
     */
650
    public function create(CreateStruct $createStruct, array $parentNode)
651
    {
652
        $location = new Location();
653
        /** @var $query \eZ\Publish\Core\Persistence\Database\InsertQuery */
654
        $query = $this->handler->createInsertQuery();
655
        $query
656
            ->insertInto($this->handler->quoteTable('ezcontentobject_tree'))
657
            ->set(
658
                $this->handler->quoteColumn('contentobject_id'),
659
                $query->bindValue($location->contentId = $createStruct->contentId, null, \PDO::PARAM_INT)
660
            )->set(
661
                $this->handler->quoteColumn('contentobject_is_published'),
662
                $query->bindValue(1, null, \PDO::PARAM_INT)
663
            )->set(
664
                $this->handler->quoteColumn('contentobject_version'),
665
                $query->bindValue($createStruct->contentVersion, null, \PDO::PARAM_INT)
666
            )->set(
667
                $this->handler->quoteColumn('depth'),
668
                $query->bindValue($location->depth = $parentNode['depth'] + 1, null, \PDO::PARAM_INT)
669
            )->set(
670
                $this->handler->quoteColumn('is_hidden'),
671
                $query->bindValue($location->hidden = $createStruct->hidden, null, \PDO::PARAM_INT)
672
            )->set(
673
                $this->handler->quoteColumn('is_invisible'),
674
                $query->bindValue($location->invisible = $createStruct->invisible, null, \PDO::PARAM_INT)
675
            )->set(
676
                $this->handler->quoteColumn('modified_subnode'),
677
                $query->bindValue(time(), null, \PDO::PARAM_INT)
678
            )->set(
679
                $this->handler->quoteColumn('node_id'),
680
                $this->handler->getAutoIncrementValue('ezcontentobject_tree', 'node_id')
681
            )->set(
682
                $this->handler->quoteColumn('parent_node_id'),
683
                $query->bindValue($location->parentId = $parentNode['node_id'], null, \PDO::PARAM_INT)
684
            )->set(
685
                $this->handler->quoteColumn('path_identification_string'),
686
                $query->bindValue($location->pathIdentificationString = $createStruct->pathIdentificationString, null, \PDO::PARAM_STR)
0 ignored issues
show
Deprecated Code introduced by
The property eZ\Publish\SPI\Persisten...athIdentificationString has been deprecated with message: Since 5.4, planned to be removed in 6.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
Deprecated Code introduced by
The property eZ\Publish\SPI\Persisten...athIdentificationString has been deprecated with message: Since 5.4, planned to be removed in 6.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
687
            )->set(
688
                $this->handler->quoteColumn('path_string'),
689
                $query->bindValue('dummy') // Set later
690
            )->set(
691
                $this->handler->quoteColumn('priority'),
692
                $query->bindValue($location->priority = $createStruct->priority, null, \PDO::PARAM_INT)
693
            )->set(
694
                $this->handler->quoteColumn('remote_id'),
695
                $query->bindValue($location->remoteId = $createStruct->remoteId, null, \PDO::PARAM_STR)
696
            )->set(
697
                $this->handler->quoteColumn('sort_field'),
698
                $query->bindValue($location->sortField = $createStruct->sortField, null, \PDO::PARAM_INT)
699
            )->set(
700
                $this->handler->quoteColumn('sort_order'),
701
                $query->bindValue($location->sortOrder = $createStruct->sortOrder, null, \PDO::PARAM_INT)
702
            );
703
        $query->prepare()->execute();
704
705
        $location->id = $this->handler->lastInsertId($this->handler->getSequenceName('ezcontentobject_tree', 'node_id'));
706
707
        $mainLocationId = $createStruct->mainLocationId === true ? $location->id : $createStruct->mainLocationId;
708
        $location->pathString = $parentNode['path_string'] . $location->id . '/';
709
        /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
710
        $query = $this->handler->createUpdateQuery();
711
        $query
712
            ->update($this->handler->quoteTable('ezcontentobject_tree'))
713
            ->set(
714
                $this->handler->quoteColumn('path_string'),
715
                $query->bindValue($location->pathString)
716
            )
717
            ->set(
718
                $this->handler->quoteColumn('main_node_id'),
719
                $query->bindValue($mainLocationId, null, \PDO::PARAM_INT)
720
            )
721
            ->where(
722
                $query->expr->eq(
723
                    $this->handler->quoteColumn('node_id'),
724
                    $query->bindValue($location->id, null, \PDO::PARAM_INT)
725
                )
726
            );
727
        $query->prepare()->execute();
728
729
        return $location;
730
    }
731
732
    /**
733
     * Create an entry in the node assignment table.
734
     *
735
     * @param \eZ\Publish\SPI\Persistence\Content\Location\CreateStruct $createStruct
736
     * @param mixed $parentNodeId
737
     * @param int $type
738
     */
739
    public function createNodeAssignment(CreateStruct $createStruct, $parentNodeId, $type = self::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP)
740
    {
741
        $isMain = ($createStruct->mainLocationId === true ? 1 : 0);
742
743
        $query = $this->handler->createInsertQuery();
744
        $query
745
            ->insertInto($this->handler->quoteTable('eznode_assignment'))
746
            ->set(
747
                $this->handler->quoteColumn('contentobject_id'),
748
                $query->bindValue($createStruct->contentId, null, \PDO::PARAM_INT)
749
            )->set(
750
                $this->handler->quoteColumn('contentobject_version'),
751
                $query->bindValue($createStruct->contentVersion, null, \PDO::PARAM_INT)
752
            )->set(
753
                $this->handler->quoteColumn('from_node_id'),
754
                $query->bindValue(0, null, \PDO::PARAM_INT) // unused field
755
            )->set(
756
                $this->handler->quoteColumn('id'),
757
                $this->handler->getAutoIncrementValue('eznode_assignment', 'id')
758
            )->set(
759
                $this->handler->quoteColumn('is_main'),
760
                $query->bindValue($isMain, null, \PDO::PARAM_INT) // Changed by the business layer, later
761
            )->set(
762
                $this->handler->quoteColumn('op_code'),
763
                $query->bindValue($type, null, \PDO::PARAM_INT)
764
            )->set(
765
                $this->handler->quoteColumn('parent_node'),
766
                $query->bindValue($parentNodeId, null, \PDO::PARAM_INT)
767
            )->set(
768
                // parent_remote_id column should contain the remote id of the corresponding Location
769
                $this->handler->quoteColumn('parent_remote_id'),
770
                $query->bindValue($createStruct->remoteId, null, \PDO::PARAM_STR)
771
            )->set(
772
                // remote_id column should contain the remote id of the node assignment itself,
773
                // however this was never implemented completely in Legacy Stack, so we just set
774
                // it to default value '0'
775
                $this->handler->quoteColumn('remote_id'),
776
                $query->bindValue('0', null, \PDO::PARAM_STR)
777
            )->set(
778
                $this->handler->quoteColumn('sort_field'),
779
                $query->bindValue($createStruct->sortField, null, \PDO::PARAM_INT)
780
            )->set(
781
                $this->handler->quoteColumn('sort_order'),
782
                $query->bindValue($createStruct->sortOrder, null, \PDO::PARAM_INT)
783
            )->set(
784
                $this->handler->quoteColumn('priority'),
785
                $query->bindValue($createStruct->priority, null, \PDO::PARAM_INT)
786
            )->set(
787
                $this->handler->quoteColumn('is_hidden'),
788
                $query->bindValue($createStruct->hidden, null, \PDO::PARAM_INT)
789
            );
790
        $query->prepare()->execute();
791
    }
792
793
    /**
794
     * Deletes node assignment for given $contentId and $versionNo.
795
     *
796
     * If $versionNo is not passed all node assignments for given $contentId are deleted
797
     *
798
     * @param int $contentId
799
     * @param int|null $versionNo
800
     */
801
    public function deleteNodeAssignment($contentId, $versionNo = null)
802
    {
803
        $query = $this->handler->createDeleteQuery();
804
        $query->deleteFrom(
805
            'eznode_assignment'
806
        )->where(
807
            $query->expr->eq(
808
                $this->handler->quoteColumn('contentobject_id'),
809
                $query->bindValue($contentId, null, \PDO::PARAM_INT)
810
            )
811
        );
812
        if (isset($versionNo)) {
813
            $query->where(
814
                $query->expr->eq(
815
                    $this->handler->quoteColumn('contentobject_version'),
816
                    $query->bindValue($versionNo, null, \PDO::PARAM_INT)
817
                )
818
            );
819
        }
820
        $query->prepare()->execute();
821
    }
822
823
    /**
824
     * Update node assignment table.
825
     *
826
     * @param int $contentObjectId
827
     * @param int $oldParent
828
     * @param int $newParent
829
     * @param int $opcode
830
     */
831 View Code Duplication
    public function updateNodeAssignment($contentObjectId, $oldParent, $newParent, $opcode)
832
    {
833
        $query = $this->handler->createUpdateQuery();
834
        $query
835
            ->update($this->handler->quoteTable('eznode_assignment'))
836
            ->set(
837
                $this->handler->quoteColumn('parent_node'),
838
                $query->bindValue($newParent, null, \PDO::PARAM_INT)
839
            )
840
            ->set(
841
                $this->handler->quoteColumn('op_code'),
842
                $query->bindValue($opcode, null, \PDO::PARAM_INT)
843
            )
844
            ->where(
845
                $query->expr->lAnd(
846
                    $query->expr->eq(
847
                        $this->handler->quoteColumn('contentobject_id'),
848
                        $query->bindValue($contentObjectId, null, \PDO::PARAM_INT)
849
                    ),
850
                    $query->expr->eq(
851
                        $this->handler->quoteColumn('parent_node'),
852
                        $query->bindValue($oldParent, null, \PDO::PARAM_INT)
853
                    )
854
                )
855
            );
856
        $query->prepare()->execute();
857
    }
858
859
    /**
860
     * Create locations from node assignments.
861
     *
862
     * Convert existing node assignments into real locations.
863
     *
864
     * @param mixed $contentId
865
     * @param mixed $versionNo
866
     */
867
    public function createLocationsFromNodeAssignments($contentId, $versionNo)
868
    {
869
        // select all node assignments with OP_CODE_CREATE (3) for this content
870
        $query = $this->handler->createSelectQuery();
871
        $query
872
            ->select('*')
873
            ->from($this->handler->quoteTable('eznode_assignment'))
874
            ->where(
875
                $query->expr->lAnd(
876
                    $query->expr->eq(
877
                        $this->handler->quoteColumn('contentobject_id'),
878
                        $query->bindValue($contentId, null, \PDO::PARAM_INT)
879
                    ),
880
                    $query->expr->eq(
881
                        $this->handler->quoteColumn('contentobject_version'),
882
                        $query->bindValue($versionNo, null, \PDO::PARAM_INT)
883
                    ),
884
                    $query->expr->eq(
885
                        $this->handler->quoteColumn('op_code'),
886
                        $query->bindValue(self::NODE_ASSIGNMENT_OP_CODE_CREATE, null, \PDO::PARAM_INT)
887
                    )
888
                )
889
            )->orderBy('id');
890
        $statement = $query->prepare();
891
        $statement->execute();
892
893
        // convert all these assignments to nodes
894
895
        while ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
896
            if ((bool)$row['is_main'] === true) {
897
                $mainLocationId = true;
898
            } else {
899
                $mainLocationId = $this->getMainNodeId($contentId);
900
            }
901
902
            $parentLocationData = $this->getBasicNodeData($row['parent_node']);
903
            $isInvisible = $row['is_hidden'] || $parentLocationData['is_hidden'] || $parentLocationData['is_invisible'];
904
            $this->create(
905
                new CreateStruct(
906
                    array(
907
                        'contentId' => $row['contentobject_id'],
908
                        'contentVersion' => $row['contentobject_version'],
909
                        'mainLocationId' => $mainLocationId,
910
                        'remoteId' => $row['parent_remote_id'],
911
                        'sortField' => $row['sort_field'],
912
                        'sortOrder' => $row['sort_order'],
913
                        'priority' => $row['priority'],
914
                        'hidden' => $row['is_hidden'],
915
                        'invisible' => $isInvisible,
916
                    )
917
                ),
918
                $parentLocationData
919
            );
920
921
            $this->updateNodeAssignment(
922
                $row['contentobject_id'],
923
                $row['parent_node'],
924
                $row['parent_node'],
925
                self::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP
926
            );
927
        }
928
    }
929
930
    /**
931
     * Updates all Locations of content identified with $contentId with $versionNo.
932
     *
933
     * @param mixed $contentId
934
     * @param mixed $versionNo
935
     */
936 View Code Duplication
    public function updateLocationsContentVersionNo($contentId, $versionNo)
937
    {
938
        $query = $this->handler->createUpdateQuery();
939
        $query->update(
940
            $this->handler->quoteTable('ezcontentobject_tree')
941
        )->set(
942
            $this->handler->quoteColumn('contentobject_version'),
943
            $query->bindValue($versionNo, null, \PDO::PARAM_INT)
944
        )->where(
945
            $query->expr->eq(
946
                $this->handler->quoteColumn('contentobject_id'),
947
                $contentId
948
            )
949
        );
950
        $query->prepare()->execute();
951
    }
952
953
    /**
954
     * Searches for the main nodeId of $contentId in $versionId.
955
     *
956
     * @param int $contentId
957
     *
958
     * @return int|bool
959
     */
960
    private function getMainNodeId($contentId)
961
    {
962
        $query = $this->handler->createSelectQuery();
963
        $query
964
            ->select('node_id')
965
            ->from($this->handler->quoteTable('ezcontentobject_tree'))
966
            ->where(
967
                $query->expr->lAnd(
968
                    $query->expr->eq(
969
                        $this->handler->quoteColumn('contentobject_id'),
970
                        $query->bindValue($contentId, null, \PDO::PARAM_INT)
971
                    ),
972
                    $query->expr->eq(
973
                        $this->handler->quoteColumn('node_id'),
974
                        $this->handler->quoteColumn('main_node_id')
975
                    )
976
                )
977
            );
978
        $statement = $query->prepare();
979
        $statement->execute();
980
981
        $result = $statement->fetchAll(\PDO::FETCH_ASSOC);
982
        if (count($result) === 1) {
983
            return (int)$result[0]['node_id'];
984
        } else {
985
            return false;
986
        }
987
    }
988
989
    /**
990
     * Updates an existing location.
991
     *
992
     * Will not throw anything if location id is invalid or no entries are affected.
993
     *
994
     * @param \eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct $location
995
     * @param int $locationId
996
     */
997
    public function update(UpdateStruct $location, $locationId)
998
    {
999
        $query = $this->handler->createUpdateQuery();
1000
1001
        $query
1002
            ->update($this->handler->quoteTable('ezcontentobject_tree'))
1003
            ->set(
1004
                $this->handler->quoteColumn('priority'),
1005
                $query->bindValue($location->priority)
1006
            )
1007
            ->set(
1008
                $this->handler->quoteColumn('remote_id'),
1009
                $query->bindValue($location->remoteId)
1010
            )
1011
            ->set(
1012
                $this->handler->quoteColumn('sort_order'),
1013
                $query->bindValue($location->sortOrder)
1014
            )
1015
            ->set(
1016
                $this->handler->quoteColumn('sort_field'),
1017
                $query->bindValue($location->sortField)
1018
            )
1019
            ->where(
1020
                $query->expr->eq(
1021
                    $this->handler->quoteColumn('node_id'),
1022
                    $locationId
1023
                )
1024
            );
1025
        $statement = $query->prepare();
1026
        $statement->execute();
1027
1028
        // Commented due to EZP-23302: Update Location fails if no change is performed with the update
1029
        // Should be fixed with PDO::MYSQL_ATTR_FOUND_ROWS instead
1030
        /*if ( $statement->rowCount() < 1 )
1031
        {
1032
            throw new NotFound( 'location', $locationId );
1033
        }*/
1034
    }
1035
1036
    /**
1037
     * Updates path identification string for given $locationId.
1038
     *
1039
     * @param mixed $locationId
1040
     * @param mixed $parentLocationId
1041
     * @param string $text
1042
     */
1043
    public function updatePathIdentificationString($locationId, $parentLocationId, $text)
1044
    {
1045
        $parentData = $this->getBasicNodeData($parentLocationId);
1046
1047
        $newPathIdentificationString = empty($parentData['path_identification_string']) ?
1048
            $text :
1049
            $parentData['path_identification_string'] . '/' . $text;
1050
1051
        /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
1052
        $query = $this->handler->createUpdateQuery();
1053
        $query->update(
1054
            'ezcontentobject_tree'
1055
        )->set(
1056
            $this->handler->quoteColumn('path_identification_string'),
1057
            $query->bindValue($newPathIdentificationString, null, \PDO::PARAM_STR)
1058
        )->where(
1059
            $query->expr->eq(
1060
                $this->handler->quoteColumn('node_id'),
1061
                $query->bindValue($locationId, null, \PDO::PARAM_INT)
1062
            )
1063
        );
1064
        $query->prepare()->execute();
1065
    }
1066
1067
    /**
1068
     * Deletes ezcontentobject_tree row for given $locationId (node_id).
1069
     *
1070
     * @param mixed $locationId
1071
     */
1072
    public function removeLocation($locationId)
1073
    {
1074
        $query = $this->handler->createDeleteQuery();
1075
        $query->deleteFrom(
1076
            'ezcontentobject_tree'
1077
        )->where(
1078
            $query->expr->eq(
1079
                $this->handler->quoteColumn('node_id'),
1080
                $query->bindValue($locationId, null, \PDO::PARAM_INT)
1081
            )
1082
        );
1083
        $query->prepare()->execute();
1084
    }
1085
1086
    /**
1087
     * Returns id of the next in line node to be set as a new main node.
1088
     *
1089
     * This returns lowest node id for content identified by $contentId, and not of
1090
     * the node identified by given $locationId (current main node).
1091
     * Assumes that content has more than one location.
1092
     *
1093
     * @param mixed $contentId
1094
     * @param mixed $locationId
1095
     *
1096
     * @return array
1097
     */
1098
    public function getFallbackMainNodeData($contentId, $locationId)
1099
    {
1100
        $query = $this->handler->createSelectQuery();
1101
        $query->select(
1102
            $this->handler->quoteColumn('node_id'),
1103
            $this->handler->quoteColumn('contentobject_version'),
1104
            $this->handler->quoteColumn('parent_node_id')
1105
        )->from(
1106
            $this->handler->quoteTable('ezcontentobject_tree')
1107
        )->where(
1108
            $query->expr->lAnd(
1109
                $query->expr->eq(
1110
                    $this->handler->quoteColumn('contentobject_id'),
1111
                    $query->bindValue($contentId, null, \PDO::PARAM_INT)
1112
                ),
1113
                $query->expr->neq(
1114
                    $this->handler->quoteColumn('node_id'),
1115
                    $query->bindValue($locationId, null, \PDO::PARAM_INT)
1116
                )
1117
            )
1118
        )->orderBy('node_id', SelectQuery::ASC)->limit(1);
1119
        $statement = $query->prepare();
1120
        $statement->execute();
1121
1122
        return $statement->fetch(\PDO::FETCH_ASSOC);
1123
    }
1124
1125
    /**
1126
     * Sends a single location identified by given $locationId to the trash.
1127
     *
1128
     * The associated content object is left untouched.
1129
     *
1130
     * @param mixed $locationId
1131
     *
1132
     * @return bool
1133
     */
1134
    public function trashLocation($locationId)
1135
    {
1136
        $locationRow = $this->getBasicNodeData($locationId);
1137
1138
        /** @var $query \eZ\Publish\Core\Persistence\Database\InsertQuery */
1139
        $query = $this->handler->createInsertQuery();
1140
        $query->insertInto($this->handler->quoteTable('ezcontentobject_trash'));
1141
1142
        unset($locationRow['contentobject_is_published']);
1143
        foreach ($locationRow as $key => $value) {
1144
            $query->set($key, $query->bindValue($value));
1145
        }
1146
1147
        $query->prepare()->execute();
1148
1149
        $this->removeLocation($locationRow['node_id']);
1150
        $this->setContentStatus($locationRow['contentobject_id'], ContentInfo::STATUS_ARCHIVED);
1151
    }
1152
1153
    /**
1154
     * Returns a trashed location to normal state.
1155
     *
1156
     * Recreates the originally trashed location in the new position. If no new
1157
     * position has been specified, it will be tried to re-create the location
1158
     * at the old position. If this is not possible ( because the old location
1159
     * does not exist any more) and exception is thrown.
1160
     *
1161
     * @param mixed $locationId
1162
     * @param mixed|null $newParentId
1163
     *
1164
     * @return \eZ\Publish\SPI\Persistence\Content\Location
1165
     */
1166
    public function untrashLocation($locationId, $newParentId = null)
1167
    {
1168
        $row = $this->loadTrashByLocation($locationId);
1169
1170
        $newLocation = $this->create(
1171
            new CreateStruct(
1172
                array(
1173
                    'priority' => $row['priority'],
1174
                    'hidden' => $row['is_hidden'],
1175
                    'invisible' => $row['is_invisible'],
1176
                    'remoteId' => $row['remote_id'],
1177
                    'contentId' => $row['contentobject_id'],
1178
                    'contentVersion' => $row['contentobject_version'],
1179
                    'mainLocationId' => true, // Restored location is always main location
1180
                    'sortField' => $row['sort_field'],
1181
                    'sortOrder' => $row['sort_order'],
1182
                )
1183
            ),
1184
            $this->getBasicNodeData($newParentId ?: $row['parent_node_id'])
1185
        );
1186
1187
        $this->removeElementFromTrash($locationId);
1188
        $this->setContentStatus($row['contentobject_id'], ContentInfo::STATUS_PUBLISHED);
1189
1190
        return $newLocation;
1191
    }
1192
1193
    /**
1194
     * @param mixed $contentId
1195
     * @param int $status
1196
     */
1197
    protected function setContentStatus($contentId, $status)
1198
    {
1199
        /** @var $query \eZ\Publish\Core\Persistence\Database\UpdateQuery */
1200
        $query = $this->handler->createUpdateQuery();
1201
        $query->update(
1202
            'ezcontentobject'
1203
        )->set(
1204
            $this->handler->quoteColumn('status'),
1205
            $query->bindValue($status, null, \PDO::PARAM_INT)
1206
        )->where(
1207
            $query->expr->eq(
1208
                $this->handler->quoteColumn('id'),
1209
                $query->bindValue($contentId, null, \PDO::PARAM_INT)
1210
            )
1211
        );
1212
        $query->prepare()->execute();
1213
    }
1214
1215
    /**
1216
     * Loads trash data specified by location ID.
1217
     *
1218
     * @param mixed $locationId
1219
     *
1220
     * @return array
1221
     */
1222 View Code Duplication
    public function loadTrashByLocation($locationId)
1223
    {
1224
        $query = $this->handler->createSelectQuery();
1225
        $query
1226
            ->select('*')
1227
            ->from($this->handler->quoteTable('ezcontentobject_trash'))
1228
            ->where(
1229
                $query->expr->eq(
1230
                    $this->handler->quoteColumn('node_id'),
1231
                    $query->bindValue($locationId)
1232
                )
1233
            );
1234
        $statement = $query->prepare();
1235
        $statement->execute();
1236
1237
        if ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
1238
            return $row;
1239
        }
1240
1241
        throw new NotFound('trash', $locationId);
1242
    }
1243
1244
    /**
1245
     * List trashed items.
1246
     *
1247
     * @param int $offset
1248
     * @param int $limit
1249
     * @param array $sort
1250
     *
1251
     * @return array
1252
     */
1253
    public function listTrashed($offset, $limit, array $sort = null)
1254
    {
1255
        $query = $this->handler->createSelectQuery();
1256
        $query
1257
            ->select('*')
1258
            ->from($this->handler->quoteTable('ezcontentobject_trash'));
1259
1260
        $sort = $sort ?: array();
1261
        foreach ($sort as $condition) {
1262
            $sortDirection = $condition->direction === Query::SORT_ASC ? SelectQuery::ASC : SelectQuery::DESC;
1263
            switch (true) {
1264
                case $condition instanceof SortClause\Location\Depth:
1265
                    $query->orderBy('depth', $sortDirection);
1266
                    break;
1267
1268
                case $condition instanceof SortClause\Location\Path:
1269
                    $query->orderBy('path_string', $sortDirection);
1270
                    break;
1271
1272
                case $condition instanceof SortClause\Location\Priority:
1273
                    $query->orderBy('priority', $sortDirection);
1274
                    break;
1275
1276
                default:
1277
                    // Only handle location related sort clauses. The others
1278
                    // require data aggregation which is not sensible here.
1279
                    // Since also criteria are yet ignored, because they are
1280
                    // simply not used yet in eZ Publish, we skip that for now.
1281
                    throw new RuntimeException('Unhandled sort clause: ' . get_class($condition));
1282
            }
1283
        }
1284
1285
        if ($limit !== null) {
1286
            $query->limit($limit, $offset);
1287
        }
1288
1289
        $statement = $query->prepare();
1290
        $statement->execute();
1291
1292
        $rows = array();
1293
        while ($row = $statement->fetch(\PDO::FETCH_ASSOC)) {
1294
            $rows[] = $row;
1295
        }
1296
1297
        return $rows;
1298
    }
1299
1300
    /**
1301
     * Removes every entries in the trash.
1302
     * Will NOT remove associated content objects nor attributes.
1303
     *
1304
     * Basically truncates ezcontentobject_trash table.
1305
     */
1306
    public function cleanupTrash()
1307
    {
1308
        $query = $this->handler->createDeleteQuery();
1309
        $query->deleteFrom('ezcontentobject_trash');
1310
        $query->prepare()->execute();
1311
    }
1312
1313
    /**
1314
     * Removes trashed element identified by $id from trash.
1315
     * Will NOT remove associated content object nor attributes.
1316
     *
1317
     * @param int $id The trashed location Id
1318
     */
1319
    public function removeElementFromTrash($id)
1320
    {
1321
        $query = $this->handler->createDeleteQuery();
1322
        $query
1323
            ->deleteFrom('ezcontentobject_trash')
1324
            ->where(
1325
                $query->expr->eq(
1326
                    $this->handler->quoteColumn('node_id'),
1327
                    $query->bindValue($id, null, \PDO::PARAM_INT)
1328
                )
1329
            );
1330
        $query->prepare()->execute();
1331
    }
1332
1333
    /**
1334
     * Set section on all content objects in the subtree.
1335
     *
1336
     * @param mixed $pathString
1337
     * @param mixed $sectionId
1338
     *
1339
     * @return bool
1340
     */
1341
    public function setSectionForSubtree($pathString, $sectionId)
1342
    {
1343
        $query = $this->handler->createUpdateQuery();
1344
1345
        $subSelect = $query->subSelect();
1346
        $subSelect
1347
            ->select($this->handler->quoteColumn('contentobject_id'))
1348
            ->from($this->handler->quoteTable('ezcontentobject_tree'))
1349
            ->where(
1350
                $subSelect->expr->like(
1351
                    $this->handler->quoteColumn('path_string'),
1352
                    $subSelect->bindValue($pathString . '%')
1353
                )
1354
            );
1355
1356
        $query
1357
            ->update($this->handler->quoteTable('ezcontentobject'))
1358
            ->set(
1359
                $this->handler->quoteColumn('section_id'),
1360
                $query->bindValue($sectionId)
1361
            )
1362
            ->where(
1363
                $query->expr->in(
1364
                    $this->handler->quoteColumn('id'),
1365
                    $subSelect
1366
                )
1367
            );
1368
        $query->prepare()->execute();
1369
    }
1370
1371
    /**
1372
     * Returns how many locations given content object identified by $contentId has.
1373
     *
1374
     * @param int $contentId
1375
     *
1376
     * @return int
1377
     */
1378
    public function countLocationsByContentId($contentId)
1379
    {
1380
        $q = $this->handler->createSelectQuery();
1381
        $q
1382
            ->select(
1383
                $q->alias($q->expr->count('*'), 'count')
1384
            )
1385
            ->from($this->handler->quoteTable('ezcontentobject_tree'))
1386
            ->where(
1387
                $q->expr->eq(
1388
                    $this->handler->quoteColumn('contentobject_id'),
1389
                    $q->bindValue($contentId, null, \PDO::PARAM_INT)
1390
                )
1391
            );
1392
        $stmt = $q->prepare();
1393
        $stmt->execute();
1394
        $res = $stmt->fetchAll(\PDO::FETCH_ASSOC);
1395
1396
        return (int)$res[0]['count'];
1397
    }
1398
1399
    /**
1400
     * Changes main location of content identified by given $contentId to location identified by given $locationId.
1401
     *
1402
     * Updates ezcontentobject_tree table for the given $contentId and eznode_assignment table for the given
1403
     * $contentId, $parentLocationId and $versionNo
1404
     *
1405
     * @param mixed $contentId
1406
     * @param mixed $locationId
1407
     * @param mixed $versionNo version number, needed to update eznode_assignment table
1408
     * @param mixed $parentLocationId parent location of location identified by $locationId, needed to update
1409
     *        eznode_assignment table
1410
     */
1411
    public function changeMainLocation($contentId, $locationId, $versionNo, $parentLocationId)
1412
    {
1413
        // Update ezcontentobject_tree table
1414
        $q = $this->handler->createUpdateQuery();
1415
        $q->update(
1416
            $this->handler->quoteTable('ezcontentobject_tree')
1417
        )->set(
1418
            $this->handler->quoteColumn('main_node_id'),
1419
            $q->bindValue($locationId, null, \PDO::PARAM_INT)
1420
        )->where(
1421
            $q->expr->eq(
1422
                $this->handler->quoteColumn('contentobject_id'),
1423
                $q->bindValue($contentId, null, \PDO::PARAM_INT)
1424
            )
1425
        );
1426
        $q->prepare()->execute();
1427
1428
        // Erase is_main in eznode_assignment table
1429
        $q = $this->handler->createUpdateQuery();
1430
        $q->update(
1431
            $this->handler->quoteTable('eznode_assignment')
1432
        )->set(
1433
            $this->handler->quoteColumn('is_main'),
1434
            $q->bindValue(0, null, \PDO::PARAM_INT)
1435
        )->where(
1436
            $q->expr->lAnd(
1437
                $q->expr->eq(
1438
                    $this->handler->quoteColumn('contentobject_id'),
1439
                    $q->bindValue($contentId, null, \PDO::PARAM_INT)
1440
                ),
1441
                $q->expr->eq(
1442
                    $this->handler->quoteColumn('contentobject_version'),
1443
                    $q->bindValue($versionNo, null, \PDO::PARAM_INT)
1444
                ),
1445
                $q->expr->neq(
1446
                    $this->handler->quoteColumn('parent_node'),
1447
                    $q->bindValue($parentLocationId, null, \PDO::PARAM_INT)
1448
                )
1449
            )
1450
        );
1451
        $q->prepare()->execute();
1452
1453
        // Set new is_main in eznode_assignment table
1454
        $q = $this->handler->createUpdateQuery();
1455
        $q->update(
1456
            $this->handler->quoteTable('eznode_assignment')
1457
        )->set(
1458
            $this->handler->quoteColumn('is_main'),
1459
            $q->bindValue(1, null, \PDO::PARAM_INT)
1460
        )->where(
1461
            $q->expr->lAnd(
1462
                $q->expr->eq(
1463
                    $this->handler->quoteColumn('contentobject_id'),
1464
                    $q->bindValue($contentId, null, \PDO::PARAM_INT)
1465
                ),
1466
                $q->expr->eq(
1467
                    $this->handler->quoteColumn('contentobject_version'),
1468
                    $q->bindValue($versionNo, null, \PDO::PARAM_INT)
1469
                ),
1470
                $q->expr->eq(
1471
                    $this->handler->quoteColumn('parent_node'),
1472
                    $q->bindValue($parentLocationId, null, \PDO::PARAM_INT)
1473
                )
1474
            )
1475
        );
1476
        $q->prepare()->execute();
1477
    }
1478
}
1479