Completed
Push — EZEE-3159 ( 9c86bd )
by
unknown
19:29
created

Handler::loadLocationsByTrashContent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 6
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
5
 * @license For full copyright and license information view LICENSE file distributed with this source code.
6
 */
7
namespace eZ\Publish\Core\Persistence\Legacy\Content\Location;
8
9
use eZ\Publish\SPI\Persistence\Content;
10
use eZ\Publish\SPI\Persistence\Content\Location;
11
use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct;
12
use eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct;
13
use eZ\Publish\SPI\Persistence\Content\Location\Handler as BaseLocationHandler;
14
use eZ\Publish\Core\Persistence\Legacy\Content\Handler as ContentHandler;
15
use eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler;
16
use eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler as ObjectStateHandler;
17
use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway;
18
use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper;
19
use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct;
20
use eZ\Publish\SPI\Persistence\Content\Location\Trashed;
21
22
/**
23
 * The Location Handler interface defines operations on Location elements in the storage engine.
24
 */
25
class Handler implements BaseLocationHandler
26
{
27
    /**
28
     * Gateway for handling location data.
29
     *
30
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway
31
     */
32
    protected $locationGateway;
33
34
    /**
35
     * Location locationMapper.
36
     *
37
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper
38
     */
39
    protected $locationMapper;
40
41
    /**
42
     * Content handler.
43
     *
44
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Handler
45
     */
46
    protected $contentHandler;
47
48
    /**
49
     * Object state handler.
50
     *
51
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler
52
     */
53
    protected $objectStateHandler;
54
55
    /**
56
     * Tree handler.
57
     *
58
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler
59
     */
60
    protected $treeHandler;
61
62
    /**
63
     * Construct from userGateway.
64
     *
65
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway $locationGateway
66
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper $locationMapper
67
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\Handler $contentHandler
68
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler $objectStateHandler
69
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler $treeHandler
70
     *
71
     * @return \eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
72
     */
73
    public function __construct(
74
        LocationGateway $locationGateway,
75
        LocationMapper $locationMapper,
76
        ContentHandler $contentHandler,
77
        ObjectStateHandler $objectStateHandler,
78
        TreeHandler $treeHandler
79
    ) {
80
        $this->locationGateway = $locationGateway;
81
        $this->locationMapper = $locationMapper;
82
        $this->contentHandler = $contentHandler;
83
        $this->objectStateHandler = $objectStateHandler;
84
        $this->treeHandler = $treeHandler;
85
    }
86
87
    /**
88
     * Returns parent path string for a path string.
89
     *
90
     * @param string $pathString
91
     *
92
     * @return string
93
     */
94
    protected function getParentPathString($pathString)
95
    {
96
        return implode('/', array_slice(explode('/', $pathString), 0, -2)) . '/';
97
    }
98
99
    /**
100
     * {@inheritdoc}
101
     */
102
    public function load($locationId, array $translations = null, bool $useAlwaysAvailable = true)
103
    {
104
        return $this->treeHandler->loadLocation($locationId, $translations, $useAlwaysAvailable);
0 ignored issues
show
Bug introduced by
It seems like $translations defined by parameter $translations on line 102 can also be of type array; however, eZ\Publish\Core\Persiste...Handler::loadLocation() does only seem to accept null|array<integer,string>, maybe add an additional type check?

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

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

An additional type check may prevent trouble.

Loading history...
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     */
110
    public function loadList(array $locationIds, array $translations = null, bool $useAlwaysAvailable = true): iterable
111
    {
112
        $list = $this->locationGateway->getNodeDataList($locationIds, $translations, $useAlwaysAvailable);
0 ignored issues
show
Bug introduced by
It seems like $translations defined by parameter $translations on line 110 can also be of type array; however, eZ\Publish\Core\Persiste...eway::getNodeDataList() does only seem to accept null|array<integer,string>, maybe add an additional type check?

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

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

An additional type check may prevent trouble.

Loading history...
113
114
        $locations = [];
115
        foreach ($list as $row) {
116
            $id = (int)$row['node_id'];
117
            if (!isset($locations[$id])) {
118
                $locations[$id] = $this->locationMapper->createLocationFromRow($row);
119
            }
120
        }
121
122
        return $locations;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $locations; (array) is incompatible with the return type declared by the interface eZ\Publish\SPI\Persisten...ation\Handler::loadList of type eZ\Publish\SPI\Persisten...ntent\Location\iterable.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
123
    }
124
125
    /**
126
     * Loads the subtree ids of the location identified by $locationId.
127
     *
128
     * @param int $locationId
129
     *
130
     * @return array Location ids are in the index, Content ids in the value.
131
     */
132
    public function loadSubtreeIds($locationId)
133
    {
134
        return $this->locationGateway->getSubtreeContent($locationId, true);
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140
    public function loadByRemoteId($remoteId, array $translations = null, bool $useAlwaysAvailable = true)
141
    {
142
        $data = $this->locationGateway->getBasicNodeDataByRemoteId($remoteId, $translations, $useAlwaysAvailable);
0 ignored issues
show
Bug introduced by
It seems like $translations defined by parameter $translations on line 140 can also be of type array; however, eZ\Publish\Core\Persiste...sicNodeDataByRemoteId() does only seem to accept null|array<integer,string>, maybe add an additional type check?

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

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

An additional type check may prevent trouble.

Loading history...
143
144
        return $this->locationMapper->createLocationFromRow($data);
145
    }
146
147
    /**
148
     * Loads all locations for $contentId, optionally limited to a sub tree
149
     * identified by $rootLocationId.
150
     *
151
     * @param int $contentId
152
     * @param int $rootLocationId
153
     *
154
     * @return \eZ\Publish\SPI\Persistence\Content\Location[]
155
     */
156
    public function loadLocationsByContent($contentId, $rootLocationId = null)
157
    {
158
        $rows = $this->locationGateway->loadLocationDataByContent($contentId, $rootLocationId);
159
160
        return $this->locationMapper->createLocationsFromRows($rows);
161
    }
162
163
    /**
164
     * Loads all locations for $contentId in trash, optionally limited to a sub tree
165
     * identified by $rootLocationId.
166
     *
167
     * @param int $contentId
168
     * @param int $rootLocationId
169
     *
170
     * @return \eZ\Publish\SPI\Persistence\Content\Location[]
171
     */
172
    public function loadLocationsByTrashContent($contentId, $rootLocationId = null)
173
    {
174
        $rows = $this->locationGateway->loadLocationDataByTrashContent($contentId, $rootLocationId);
175
176
        return $this->locationMapper->createLocationsFromRows($rows, '', new Trashed());
177
    }
178
179
    /**
180
     * @see \eZ\Publish\SPI\Persistence\Content\Location\Handler::loadParentLocationsForDraftContent
181
     */
182
    public function loadParentLocationsForDraftContent($contentId)
183
    {
184
        $rows = $this->locationGateway->loadParentLocationsDataForDraftContent($contentId);
185
186
        return $this->locationMapper->createLocationsFromRows($rows);
187
    }
188
189
    /**
190
     * Returns an array of default content states with content state group id as key.
191
     *
192
     * @return \eZ\Publish\SPI\Persistence\Content\ObjectState[]
193
     */
194
    protected function getDefaultContentStates()
195
    {
196
        $defaultObjectStatesMap = [];
197
198
        foreach ($this->objectStateHandler->loadAllGroups() as $objectStateGroup) {
199
            foreach ($this->objectStateHandler->loadObjectStates($objectStateGroup->id) as $objectState) {
200
                // Only register the first object state which is the default one.
201
                $defaultObjectStatesMap[$objectStateGroup->id] = $objectState;
202
                break;
203
            }
204
        }
205
206
        return $defaultObjectStatesMap;
207
    }
208
209
    /**
210
     * @param Content $content
211
     * @param \eZ\Publish\SPI\Persistence\Content\ObjectState[] $contentStates
212
     */
213
    protected function setContentStates(Content $content, array $contentStates)
214
    {
215
        foreach ($contentStates as $contentStateGroupId => $contentState) {
216
            $this->objectStateHandler->setContentState(
217
                $content->versionInfo->contentInfo->id,
218
                $contentStateGroupId,
219
                $contentState->id
220
            );
221
        }
222
    }
223
224
    /**
225
     * Copy location object identified by $sourceId, into destination identified by $destinationParentId.
226
     *
227
     * Performs a deep copy of the location identified by $sourceId and all of
228
     * its child locations, copying the most recent published content object
229
     * for each location to a new content object without any additional version
230
     * information. Relations are not copied. URLs are not touched at all.
231
     *
232
     * @todo Either move to async/batch or find ways toward optimizing away operations per object.
233
     * @todo Optionally retain dates and set creator
234
     *
235
     * @param mixed $sourceId
236
     * @param mixed $destinationParentId
237
     * @param int|null $newOwnerId
238
     *
239
     * @return Location the newly created Location.
240
     *
241
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
242
     */
243
    public function copySubtree($sourceId, $destinationParentId, $newOwnerId = null)
244
    {
245
        $children = $this->locationGateway->getSubtreeContent($sourceId);
246
        $destinationParentData = $this->locationGateway->getBasicNodeData($destinationParentId);
247
        $defaultObjectStates = $this->getDefaultContentStates();
248
        $contentMap = [];
249
        $locationMap = [
250
            $children[0]['parent_node_id'] => [
251
                'id' => $destinationParentId,
252
                'hidden' => (bool)$destinationParentData['is_hidden'],
253
                'invisible' => (bool)$destinationParentData['is_invisible'],
254
                'path_identification_string' => $destinationParentData['path_identification_string'],
255
            ],
256
        ];
257
258
        $locations = [];
259
        foreach ($children as $child) {
260
            $locations[$child['contentobject_id']][$child['node_id']] = true;
261
        }
262
263
        $time = time();
264
        $mainLocations = [];
265
        $mainLocationsUpdate = [];
266
        foreach ($children as $index => $child) {
267
            // Copy content
268
            if (!isset($contentMap[$child['contentobject_id']])) {
269
                $content = $this->contentHandler->copy(
270
                    (int)$child['contentobject_id'],
271
                    (int)$child['contentobject_version'],
272
                    $newOwnerId
273
                );
274
275
                $this->setContentStates($content, $defaultObjectStates);
276
277
                $content = $this->contentHandler->publish(
278
                    $content->versionInfo->contentInfo->id,
279
                    $content->versionInfo->contentInfo->currentVersionNo,
280
                    new MetadataUpdateStruct(
281
                        [
282
                            'publicationDate' => $time,
283
                            'modificationDate' => $time,
284
                        ]
285
                    )
286
                );
287
288
                $contentMap[$child['contentobject_id']] = $content->versionInfo->contentInfo->id;
289
            }
290
291
            $createStruct = $this->locationMapper->getLocationCreateStruct($child);
292
            $createStruct->contentId = $contentMap[$child['contentobject_id']];
293
            $parentData = $locationMap[$child['parent_node_id']];
294
            $createStruct->parentId = $parentData['id'];
295
            $createStruct->invisible = $createStruct->hidden || $parentData['hidden'] || $parentData['invisible'];
296
            $pathString = explode('/', $child['path_identification_string']);
297
            $pathString = end($pathString);
298
            $createStruct->pathIdentificationString = strlen($pathString) > 0
299
                ? $parentData['path_identification_string'] . '/' . $pathString
300
                : null;
301
302
            // Use content main location if already set, otherwise create location as main
303
            if (isset($mainLocations[$child['contentobject_id']])) {
304
                $createStruct->mainLocationId = $locationMap[$mainLocations[$child['contentobject_id']]]['id'];
305
            } else {
306
                $createStruct->mainLocationId = true;
307
                $mainLocations[$child['contentobject_id']] = $child['node_id'];
308
309
                // If needed mark for update
310
                if (
311
                    isset($locations[$child['contentobject_id']][$child['main_node_id']]) &&
312
                    count($locations[$child['contentobject_id']]) > 1 &&
313
                    $child['node_id'] !== $child['main_node_id']
314
                ) {
315
                    $mainLocationsUpdate[$child['contentobject_id']] = $child['main_node_id'];
316
                }
317
            }
318
319
            $newLocation = $this->create($createStruct);
320
321
            $locationMap[$child['node_id']] = [
322
                'id' => $newLocation->id,
323
                'hidden' => $newLocation->hidden,
324
                'invisible' => $newLocation->invisible,
325
                'path_identification_string' => $newLocation->pathIdentificationString,
326
            ];
327
            if ($index === 0) {
328
                $copiedSubtreeRootLocation = $newLocation;
329
            }
330
        }
331
332
        // Update main locations
333
        foreach ($mainLocationsUpdate as $contentId => $mainLocationId) {
334
            $this->changeMainLocation(
335
                $contentMap[$contentId],
336
                $locationMap[$mainLocationId]['id']
337
            );
338
        }
339
340
        $destinationParentSectionId = $this->getSectionId($destinationParentId);
341
        $this->updateSubtreeSectionIfNecessary($copiedSubtreeRootLocation, $destinationParentSectionId);
0 ignored issues
show
Bug introduced by
The variable $copiedSubtreeRootLocation 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...
342
343
        return $copiedSubtreeRootLocation;
344
    }
345
346
    /**
347
     * Retrieves section ID of the location's content.
348
     *
349
     * @param int $locationId
350
     *
351
     * @return int
352
     */
353
    private function getSectionId($locationId)
354
    {
355
        $location = $this->load($locationId);
356
        $locationContentInfo = $this->contentHandler->loadContentInfo($location->contentId);
357
358
        return $locationContentInfo->sectionId;
359
    }
360
361
    /**
362
     * If the location is the main location for its content, updates subtree section.
363
     *
364
     * @param Location $location
365
     * @param int $sectionId
366
     */
367
    private function updateSubtreeSectionIfNecessary(Location $location, $sectionId)
368
    {
369
        if ($this->isMainLocation($location)) {
370
            $this->setSectionForSubtree($location->id, $sectionId);
371
        }
372
    }
373
374
    /**
375
     * Checks if the location is the main location for its content.
376
     *
377
     * @param Location $location
378
     *
379
     * @return bool
380
     */
381
    private function isMainLocation(Location $location)
382
    {
383
        $locationContentInfo = $this->contentHandler->loadContentInfo($location->contentId);
384
385
        return $locationContentInfo->mainLocationId === $location->id;
386
    }
387
388
    /**
389
     * Moves location identified by $sourceId into new parent identified by $destinationParentId.
390
     *
391
     * Performs a full move of the location identified by $sourceId to a new
392
     * destination, identified by $destinationParentId. Relations do not need
393
     * to be updated, since they refer to Content. URLs are not touched.
394
     *
395
     * @param mixed $sourceId
396
     * @param mixed $destinationParentId
397
     *
398
     * @return bool
399
     */
400
    public function move($sourceId, $destinationParentId)
401
    {
402
        $sourceNodeData = $this->locationGateway->getBasicNodeData($sourceId);
403
        $destinationNodeData = $this->locationGateway->getBasicNodeData($destinationParentId);
404
405
        $this->locationGateway->moveSubtreeNodes(
406
            $sourceNodeData,
407
            $destinationNodeData
408
        );
409
410
        $this->locationGateway->updateNodeAssignment(
411
            $sourceNodeData['contentobject_id'],
412
            $sourceNodeData['parent_node_id'],
413
            $destinationParentId,
414
            Gateway::NODE_ASSIGNMENT_OP_CODE_MOVE
415
        );
416
417
        $sourceLocation = $this->load($sourceId);
418
        $destinationParentSectionId = $this->getSectionId($destinationParentId);
419
        $this->updateSubtreeSectionIfNecessary($sourceLocation, $destinationParentSectionId);
420
    }
421
422
    /**
423
     * Marks the given nodes and all ancestors as modified.
424
     *
425
     * Optionally a time stamp with the modification date may be specified,
426
     * otherwise the current time is used.
427
     *
428
     * @param int|string $locationId
429
     * @param int $timestamp
430
     */
431
    public function markSubtreeModified($locationId, $timestamp = null)
432
    {
433
        $nodeData = $this->locationGateway->getBasicNodeData($locationId);
434
        $timestamp = $timestamp ?: time();
435
        $this->locationGateway->updateSubtreeModificationTime($nodeData['path_string'], $timestamp);
0 ignored issues
show
Deprecated Code introduced by
The method eZ\Publish\Core\Persiste...btreeModificationTime() has been deprecated with message: Not supposed to be in use anymore.

This method 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 method will be removed from the class and what other method or class to use instead.

Loading history...
436
    }
437
438
    /**
439
     * Sets a location to be hidden, and it self + all children to invisible.
440
     *
441
     * @param mixed $id Location ID
442
     */
443
    public function hide($id)
444
    {
445
        $sourceNodeData = $this->locationGateway->getBasicNodeData($id);
446
447
        $this->locationGateway->hideSubtree($sourceNodeData['path_string']);
448
    }
449
450
    /**
451
     * Sets a location to be unhidden, and self + children to visible unless a parent is hiding the tree.
452
     * If not make sure only children down to first hidden node is marked visible.
453
     *
454
     * @param mixed $id
455
     */
456
    public function unHide($id)
457
    {
458
        $sourceNodeData = $this->locationGateway->getBasicNodeData($id);
459
460
        $this->locationGateway->unhideSubtree($sourceNodeData['path_string']);
461
    }
462
463
    /**
464
     * Sets a location + all children to invisible.
465
     *
466
     * @param int $id Location ID
467
     */
468
    public function setInvisible(int $id): void
469
    {
470
        $sourceNodeData = $this->locationGateway->getBasicNodeData($id);
471
472
        $this->locationGateway->setNodeWithChildrenInvisible($sourceNodeData['path_string']);
473
    }
474
475
    /**
476
     * Sets a location + all children to visible.
477
     *
478
     * @param int $id Location ID
479
     */
480
    public function setVisible(int $id): void
481
    {
482
        $sourceNodeData = $this->locationGateway->getBasicNodeData($id);
483
484
        $this->locationGateway->setNodeWithChildrenVisible($sourceNodeData['path_string']);
485
    }
486
487
    /**
488
     * Swaps the content object being pointed to by a location object.
489
     *
490
     * Make the location identified by $locationId1 refer to the Content
491
     * referred to by $locationId2 and vice versa.
492
     *
493
     * @param mixed $locationId1
494
     * @param mixed $locationId2
495
     *
496
     * @return bool
497
     */
498
    public function swap($locationId1, $locationId2)
499
    {
500
        $this->locationGateway->swap($locationId1, $locationId2);
501
    }
502
503
    /**
504
     * Updates an existing location.
505
     *
506
     * @param \eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct $location
507
     * @param int $locationId
508
     */
509
    public function update(UpdateStruct $location, $locationId)
510
    {
511
        $this->locationGateway->update($location, $locationId);
512
    }
513
514
    /**
515
     * Creates a new location rooted at $location->parentId.
516
     *
517
     * @param \eZ\Publish\SPI\Persistence\Content\Location\CreateStruct $createStruct
518
     *
519
     * @return \eZ\Publish\SPI\Persistence\Content\Location
520
     */
521
    public function create(CreateStruct $createStruct)
522
    {
523
        $parentNodeData = $this->locationGateway->getBasicNodeData($createStruct->parentId);
524
        $locationStruct = $this->locationGateway->create($createStruct, $parentNodeData);
525
        $this->locationGateway->createNodeAssignment(
526
            $createStruct,
527
            $parentNodeData['node_id'],
528
            LocationGateway::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP
529
        );
530
531
        return $locationStruct;
532
    }
533
534
    /**
535
     * Removes all Locations under and including $locationId.
536
     *
537
     * Performs a recursive delete on the location identified by $locationId,
538
     * including all of its child locations. Content which is not referred to
539
     * by any other location is automatically removed. Content which looses its
540
     * main Location will get the first of its other Locations assigned as the
541
     * new main Location.
542
     *
543
     * @param mixed $locationId
544
     *
545
     * @return bool
546
     */
547
    public function removeSubtree($locationId)
548
    {
549
        $this->treeHandler->removeSubtree($locationId);
550
    }
551
552
    /**
553
     * Set section on all content objects in the subtree.
554
     *
555
     * @param mixed $locationId
556
     * @param mixed $sectionId
557
     */
558
    public function setSectionForSubtree($locationId, $sectionId)
559
    {
560
        $this->treeHandler->setSectionForSubtree($locationId, $sectionId);
561
    }
562
563
    /**
564
     * Changes main location of content identified by given $contentId to location identified by given $locationId.
565
     *
566
     * Updates ezcontentobject_tree and eznode_assignment tables (eznode_assignment for content current version number).
567
     *
568
     * @param mixed $contentId
569
     * @param mixed $locationId
570
     */
571
    public function changeMainLocation($contentId, $locationId)
572
    {
573
        $this->treeHandler->changeMainLocation($contentId, $locationId);
574
    }
575
576
    /**
577
     * Get the total number of all existing Locations. Can be combined with loadAllLocations.
578
     *
579
     * @return int
580
     */
581
    public function countAllLocations()
582
    {
583
        return $this->locationGateway->countAllLocations();
584
    }
585
586
    /**
587
     * Bulk-load all existing Locations, constrained by $limit and $offset to paginate results.
588
     *
589
     * @param int $offset
590
     * @param int $limit
591
     *
592
     * @return \eZ\Publish\SPI\Persistence\Content\Location[]
593
     */
594
    public function loadAllLocations($offset, $limit)
595
    {
596
        $rows = $this->locationGateway->loadAllLocationsData($offset, $limit);
597
598
        return $this->locationMapper->createLocationsFromRows($rows);
599
    }
600
}
601