Completed
Push — location_multi_load ( e5e305 )
by André
32:49 queued 12:49
created

Handler   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 535
Duplicated Lines 1.12 %

Coupling/Cohesion

Components 1
Dependencies 13

Importance

Changes 0
Metric Value
dl 6
loc 535
rs 8.8
c 0
b 0
f 0
wmc 45
lcom 1
cbo 13

26 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A getParentPathString() 0 4 1
A loadSubtreeIds() 0 4 1
A loadLocationsByContent() 0 6 1
A loadParentLocationsForDraftContent() 0 6 1
A getDefaultContentStates() 0 14 3
A setContentStates() 0 10 2
F copySubtree() 0 102 13
A getSectionId() 0 7 1
A updateSubtreeSectionIfNecessary() 0 6 2
A isMainLocation() 0 6 1
A move() 0 21 1
A markSubtreeModified() 0 6 2
A hide() 0 6 1
A unHide() 0 6 1
A swap() 0 4 1
A update() 0 4 1
A create() 0 12 1
A removeSubtree() 0 4 1
A setSectionForSubtree() 0 4 1
A changeMainLocation() 0 4 1
A countAllLocations() 0 4 1
A loadAllLocations() 0 6 1
A load() 0 4 1
A loadList() 6 14 3
A loadByRemoteId() 0 6 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Handler often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Handler, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * File containing the Location Handler class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 */
9
namespace eZ\Publish\Core\Persistence\Legacy\Content\Location;
10
11
use eZ\Publish\SPI\Persistence\Content;
12
use eZ\Publish\SPI\Persistence\Content\Location;
13
use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct;
14
use eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct;
15
use eZ\Publish\SPI\Persistence\Content\Location\Handler as BaseLocationHandler;
16
use eZ\Publish\Core\Persistence\Legacy\Content\Handler as ContentHandler;
17
use eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler;
18
use eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler as ObjectStateHandler;
19
use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway;
20
use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper;
21
use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct;
22
23
/**
24
 * The Location Handler interface defines operations on Location elements in the storage engine.
25
 */
26
class Handler implements BaseLocationHandler
27
{
28
    /**
29
     * Gateway for handling location data.
30
     *
31
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway
32
     */
33
    protected $locationGateway;
34
35
    /**
36
     * Location locationMapper.
37
     *
38
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper
39
     */
40
    protected $locationMapper;
41
42
    /**
43
     * Content handler.
44
     *
45
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\Handler
46
     */
47
    protected $contentHandler;
48
49
    /**
50
     * Object state handler.
51
     *
52
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler
53
     */
54
    protected $objectStateHandler;
55
56
    /**
57
     * Tree handler.
58
     *
59
     * @var \eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler
60
     */
61
    protected $treeHandler;
62
63
    /**
64
     * Construct from userGateway.
65
     *
66
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway $locationGateway
67
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper $locationMapper
68
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\Handler $contentHandler
69
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler $objectStateHandler
70
     * @param \eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler $treeHandler
71
     *
72
     * @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...
73
     */
74
    public function __construct(
75
        LocationGateway $locationGateway,
76
        LocationMapper $locationMapper,
77
        ContentHandler $contentHandler,
78
        ObjectStateHandler $objectStateHandler,
79
        TreeHandler $treeHandler
80
    ) {
81
        $this->locationGateway = $locationGateway;
82
        $this->locationMapper = $locationMapper;
83
        $this->contentHandler = $contentHandler;
84
        $this->objectStateHandler = $objectStateHandler;
85
        $this->treeHandler = $treeHandler;
86
    }
87
88
    /**
89
     * Returns parent path string for a path string.
90
     *
91
     * @param string $pathString
92
     *
93
     * @return string
94
     */
95
    protected function getParentPathString($pathString)
96
    {
97
        return implode('/', array_slice(explode('/', $pathString), 0, -2)) . '/';
98
    }
99
100
    /**
101
     * {@inheritdoc}
102
     */
103
    public function load($locationId, array $translations = null, bool $useAlwaysAvailable = true)
104
    {
105
        return $this->treeHandler->loadLocation($locationId, $translations, $useAlwaysAvailable);
0 ignored issues
show
Bug introduced by
It seems like $translations defined by parameter $translations on line 103 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...
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function loadList(array $locationIds, array $translations = null, bool $useAlwaysAvailable = true): iterable
112
    {
113
        $list = $this->locationGateway->getNodeDataList($locationIds, $translations, $useAlwaysAvailable);
0 ignored issues
show
Bug introduced by
It seems like $translations defined by parameter $translations on line 111 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...
114
115
        $locations = [];
116 View Code Duplication
        foreach ($list as $row) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
117
            $id = (int)$row['node_id'];
118
            if (!isset($locations[$id])) {
119
                $locations[$id] = $this->locationMapper->createLocationFromRow($row);
120
            }
121
        }
122
123
        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...
124
    }
125
126
    /**
127
     * Loads the subtree ids of the location identified by $locationId.
128
     *
129
     * @param int $locationId
130
     *
131
     * @return array Location ids are in the index, Content ids in the value.
132
     */
133
    public function loadSubtreeIds($locationId)
134
    {
135
        return $this->locationGateway->getSubtreeContent($locationId, true);
136
    }
137
138
    /**
139
     * {@inheritdoc}
140
     */
141
    public function loadByRemoteId($remoteId, array $translations = null, bool $useAlwaysAvailable = true)
142
    {
143
        $data = $this->locationGateway->getBasicNodeDataByRemoteId($remoteId, $translations, $useAlwaysAvailable);
0 ignored issues
show
Bug introduced by
It seems like $translations defined by parameter $translations on line 141 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...
144
145
        return $this->locationMapper->createLocationFromRow($data);
146
    }
147
148
    /**
149
     * Loads all locations for $contentId, optionally limited to a sub tree
150
     * identified by $rootLocationId.
151
     *
152
     * @param int $contentId
153
     * @param int $rootLocationId
154
     *
155
     * @return \eZ\Publish\SPI\Persistence\Content\Location[]
156
     */
157
    public function loadLocationsByContent($contentId, $rootLocationId = null)
158
    {
159
        $rows = $this->locationGateway->loadLocationDataByContent($contentId, $rootLocationId);
160
161
        return $this->locationMapper->createLocationsFromRows($rows);
162
    }
163
164
    /**
165
     * @see \eZ\Publish\SPI\Persistence\Content\Location\Handler::loadParentLocationsForDraftContent
166
     */
167
    public function loadParentLocationsForDraftContent($contentId)
168
    {
169
        $rows = $this->locationGateway->loadParentLocationsDataForDraftContent($contentId);
170
171
        return $this->locationMapper->createLocationsFromRows($rows);
172
    }
173
174
    /**
175
     * Returns an array of default content states with content state group id as key.
176
     *
177
     * @return \eZ\Publish\SPI\Persistence\Content\ObjectState[]
178
     */
179
    protected function getDefaultContentStates()
180
    {
181
        $defaultObjectStatesMap = array();
182
183
        foreach ($this->objectStateHandler->loadAllGroups() as $objectStateGroup) {
184
            foreach ($this->objectStateHandler->loadObjectStates($objectStateGroup->id) as $objectState) {
185
                // Only register the first object state which is the default one.
186
                $defaultObjectStatesMap[$objectStateGroup->id] = $objectState;
187
                break;
188
            }
189
        }
190
191
        return $defaultObjectStatesMap;
192
    }
193
194
    /**
195
     * @param Content $content
196
     * @param \eZ\Publish\SPI\Persistence\Content\ObjectState[] $contentStates
197
     */
198
    protected function setContentStates(Content $content, array $contentStates)
199
    {
200
        foreach ($contentStates as $contentStateGroupId => $contentState) {
201
            $this->objectStateHandler->setContentState(
202
                $content->versionInfo->contentInfo->id,
203
                $contentStateGroupId,
204
                $contentState->id
205
            );
206
        }
207
    }
208
209
    /**
210
     * Copy location object identified by $sourceId, into destination identified by $destinationParentId.
211
     *
212
     * Performs a deep copy of the location identified by $sourceId and all of
213
     * its child locations, copying the most recent published content object
214
     * for each location to a new content object without any additional version
215
     * information. Relations are not copied. URLs are not touched at all.
216
     *
217
     * @todo Either move to async/batch or find ways toward optimizing away operations per object.
218
     * @todo Optionally retain dates and set creator
219
     *
220
     * @param mixed $sourceId
221
     * @param mixed $destinationParentId
222
     * @param int|null $newOwnerId
223
     *
224
     * @return Location the newly created Location.
225
     * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
226
     */
227
    public function copySubtree($sourceId, $destinationParentId, $newOwnerId = null)
228
    {
229
        $children = $this->locationGateway->getSubtreeContent($sourceId);
230
        $destinationParentData = $this->locationGateway->getBasicNodeData($destinationParentId);
231
        $defaultObjectStates = $this->getDefaultContentStates();
232
        $contentMap = array();
233
        $locationMap = array(
234
            $children[0]['parent_node_id'] => array(
235
                'id' => $destinationParentId,
236
                'hidden' => (bool)$destinationParentData['is_hidden'],
237
                'invisible' => (bool)$destinationParentData['is_invisible'],
238
                'path_identification_string' => $destinationParentData['path_identification_string'],
239
            ),
240
        );
241
242
        $locations = array();
243
        foreach ($children as $child) {
244
            $locations[$child['contentobject_id']][$child['node_id']] = true;
245
        }
246
247
        $time = time();
248
        $mainLocations = array();
249
        $mainLocationsUpdate = array();
250
        foreach ($children as $index => $child) {
251
            // Copy content
252
            if (!isset($contentMap[$child['contentobject_id']])) {
253
                $content = $this->contentHandler->copy(
254
                    $child['contentobject_id'],
255
                    $child['contentobject_version'],
256
                    $newOwnerId
257
                );
258
259
                $this->setContentStates($content, $defaultObjectStates);
260
261
                $content = $this->contentHandler->publish(
262
                    $content->versionInfo->contentInfo->id,
263
                    $content->versionInfo->contentInfo->currentVersionNo,
264
                    new MetadataUpdateStruct(
265
                        array(
266
                            'publicationDate' => $time,
267
                            'modificationDate' => $time,
268
                        )
269
                    )
270
                );
271
272
                $contentMap[$child['contentobject_id']] = $content->versionInfo->contentInfo->id;
273
            }
274
275
            $createStruct = $this->locationMapper->getLocationCreateStruct($child);
276
            $createStruct->contentId = $contentMap[$child['contentobject_id']];
277
            $parentData = $locationMap[$child['parent_node_id']];
278
            $createStruct->parentId = $parentData['id'];
279
            $createStruct->invisible = $createStruct->hidden || $parentData['hidden'] || $parentData['invisible'];
280
            $pathString = explode('/', $child['path_identification_string']);
281
            $pathString = end($pathString);
282
            $createStruct->pathIdentificationString = strlen($pathString) > 0
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...
283
                ? $parentData['path_identification_string'] . '/' . $pathString
284
                : null;
285
286
            // Use content main location if already set, otherwise create location as main
287
            if (isset($mainLocations[$child['contentobject_id']])) {
288
                $createStruct->mainLocationId = $locationMap[$mainLocations[$child['contentobject_id']]]['id'];
289
            } else {
290
                $createStruct->mainLocationId = true;
291
                $mainLocations[$child['contentobject_id']] = $child['node_id'];
292
293
                // If needed mark for update
294
                if (
295
                    isset($locations[$child['contentobject_id']][$child['main_node_id']]) &&
296
                    count($locations[$child['contentobject_id']]) > 1 &&
297
                    $child['node_id'] !== $child['main_node_id']
298
                ) {
299
                    $mainLocationsUpdate[$child['contentobject_id']] = $child['main_node_id'];
300
                }
301
            }
302
303
            $newLocation = $this->create($createStruct);
304
305
            $locationMap[$child['node_id']] = array(
306
                'id' => $newLocation->id,
307
                'hidden' => $newLocation->hidden,
308
                'invisible' => $newLocation->invisible,
309
                'path_identification_string' => $newLocation->pathIdentificationString,
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...
310
            );
311
            if ($index === 0) {
312
                $copiedSubtreeRootLocation = $newLocation;
313
            }
314
        }
315
316
        // Update main locations
317
        foreach ($mainLocationsUpdate as $contentId => $mainLocationId) {
318
            $this->changeMainLocation(
319
                $contentMap[$contentId],
320
                $locationMap[$mainLocationId]['id']
321
            );
322
        }
323
324
        $destinationParentSectionId = $this->getSectionId($destinationParentId);
325
        $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...
326
327
        return $copiedSubtreeRootLocation;
328
    }
329
330
    /**
331
     * Retrieves section ID of the location's content.
332
     *
333
     * @param int $locationId
334
     *
335
     * @return int
336
     */
337
    private function getSectionId($locationId)
338
    {
339
        $location = $this->load($locationId);
340
        $locationContentInfo = $this->contentHandler->loadContentInfo($location->contentId);
341
342
        return $locationContentInfo->sectionId;
343
    }
344
345
    /**
346
     * If the location is the main location for its content, updates subtree section.
347
     *
348
     * @param Location $location
349
     * @param int $sectionId
350
     */
351
    private function updateSubtreeSectionIfNecessary(Location $location, $sectionId)
352
    {
353
        if ($this->isMainLocation($location)) {
354
            $this->setSectionForSubtree($location->id, $sectionId);
355
        }
356
    }
357
358
    /**
359
     * Checks if the location is the main location for its content.
360
     *
361
     * @param Location $location
362
     *
363
     * @return bool
364
     */
365
    private function isMainLocation(Location $location)
366
    {
367
        $locationContentInfo = $this->contentHandler->loadContentInfo($location->contentId);
368
369
        return $locationContentInfo->mainLocationId === $location->id;
370
    }
371
372
    /**
373
     * Moves location identified by $sourceId into new parent identified by $destinationParentId.
374
     *
375
     * Performs a full move of the location identified by $sourceId to a new
376
     * destination, identified by $destinationParentId. Relations do not need
377
     * to be updated, since they refer to Content. URLs are not touched.
378
     *
379
     * @param mixed $sourceId
380
     * @param mixed $destinationParentId
381
     *
382
     * @return bool
383
     */
384
    public function move($sourceId, $destinationParentId)
385
    {
386
        $sourceNodeData = $this->locationGateway->getBasicNodeData($sourceId);
387
        $destinationNodeData = $this->locationGateway->getBasicNodeData($destinationParentId);
388
389
        $this->locationGateway->moveSubtreeNodes(
390
            $sourceNodeData,
391
            $destinationNodeData
392
        );
393
394
        $this->locationGateway->updateNodeAssignment(
395
            $sourceNodeData['contentobject_id'],
396
            $sourceNodeData['parent_node_id'],
397
            $destinationParentId,
398
            Gateway::NODE_ASSIGNMENT_OP_CODE_MOVE
399
        );
400
401
        $sourceLocation = $this->load($sourceId);
402
        $destinationParentSectionId = $this->getSectionId($destinationParentId);
403
        $this->updateSubtreeSectionIfNecessary($sourceLocation, $destinationParentSectionId);
404
    }
405
406
    /**
407
     * Marks the given nodes and all ancestors as modified.
408
     *
409
     * Optionally a time stamp with the modification date may be specified,
410
     * otherwise the current time is used.
411
     *
412
     * @param int|string $locationId
413
     * @param int $timestamp
414
     */
415
    public function markSubtreeModified($locationId, $timestamp = null)
416
    {
417
        $nodeData = $this->locationGateway->getBasicNodeData($locationId);
418
        $timestamp = $timestamp ?: time();
419
        $this->locationGateway->updateSubtreeModificationTime($nodeData['path_string'], $timestamp);
420
    }
421
422
    /**
423
     * Sets a location to be hidden, and it self + all children to invisible.
424
     *
425
     * @param mixed $id Location ID
426
     */
427
    public function hide($id)
428
    {
429
        $sourceNodeData = $this->locationGateway->getBasicNodeData($id);
430
431
        $this->locationGateway->hideSubtree($sourceNodeData['path_string']);
432
    }
433
434
    /**
435
     * Sets a location to be unhidden, and self + children to visible unless a parent is hiding the tree.
436
     * If not make sure only children down to first hidden node is marked visible.
437
     *
438
     * @param mixed $id
439
     */
440
    public function unHide($id)
441
    {
442
        $sourceNodeData = $this->locationGateway->getBasicNodeData($id);
443
444
        $this->locationGateway->unhideSubtree($sourceNodeData['path_string']);
445
    }
446
447
    /**
448
     * Swaps the content object being pointed to by a location object.
449
     *
450
     * Make the location identified by $locationId1 refer to the Content
451
     * referred to by $locationId2 and vice versa.
452
     *
453
     * @param mixed $locationId1
454
     * @param mixed $locationId2
455
     *
456
     * @return bool
457
     */
458
    public function swap($locationId1, $locationId2)
459
    {
460
        $this->locationGateway->swap($locationId1, $locationId2);
461
    }
462
463
    /**
464
     * Updates an existing location.
465
     *
466
     * @param \eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct $location
467
     * @param int $locationId
468
     */
469
    public function update(UpdateStruct $location, $locationId)
470
    {
471
        $this->locationGateway->update($location, $locationId);
472
    }
473
474
    /**
475
     * Creates a new location rooted at $location->parentId.
476
     *
477
     * @param \eZ\Publish\SPI\Persistence\Content\Location\CreateStruct $createStruct
478
     *
479
     * @return \eZ\Publish\SPI\Persistence\Content\Location
480
     */
481
    public function create(CreateStruct $createStruct)
482
    {
483
        $parentNodeData = $this->locationGateway->getBasicNodeData($createStruct->parentId);
484
        $locationStruct = $this->locationGateway->create($createStruct, $parentNodeData);
485
        $this->locationGateway->createNodeAssignment(
486
            $createStruct,
487
            $parentNodeData['node_id'],
488
            LocationGateway::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP
489
        );
490
491
        return $locationStruct;
492
    }
493
494
    /**
495
     * Removes all Locations under and including $locationId.
496
     *
497
     * Performs a recursive delete on the location identified by $locationId,
498
     * including all of its child locations. Content which is not referred to
499
     * by any other location is automatically removed. Content which looses its
500
     * main Location will get the first of its other Locations assigned as the
501
     * new main Location.
502
     *
503
     * @param mixed $locationId
504
     *
505
     * @return bool
506
     */
507
    public function removeSubtree($locationId)
508
    {
509
        $this->treeHandler->removeSubtree($locationId);
510
    }
511
512
    /**
513
     * Set section on all content objects in the subtree.
514
     *
515
     * @param mixed $locationId
516
     * @param mixed $sectionId
517
     */
518
    public function setSectionForSubtree($locationId, $sectionId)
519
    {
520
        $this->treeHandler->setSectionForSubtree($locationId, $sectionId);
521
    }
522
523
    /**
524
     * Changes main location of content identified by given $contentId to location identified by given $locationId.
525
     *
526
     * Updates ezcontentobject_tree and eznode_assignment tables (eznode_assignment for content current version number).
527
     *
528
     * @param mixed $contentId
529
     * @param mixed $locationId
530
     */
531
    public function changeMainLocation($contentId, $locationId)
532
    {
533
        $this->treeHandler->changeMainLocation($contentId, $locationId);
534
    }
535
536
    /**
537
     * Get the total number of all existing Locations. Can be combined with loadAllLocations.
538
     *
539
     * @return int
540
     */
541
    public function countAllLocations()
542
    {
543
        return $this->locationGateway->countAllLocations();
544
    }
545
546
    /**
547
     * Bulk-load all existing Locations, constrained by $limit and $offset to paginate results.
548
     *
549
     * @param int $offset
550
     * @param int $limit
551
     *
552
     * @return \eZ\Publish\SPI\Persistence\Content\Location[]
553
     */
554
    public function loadAllLocations($offset, $limit)
555
    {
556
        $rows = $this->locationGateway->loadAllLocationsData($offset, $limit);
557
558
        return $this->locationMapper->createLocationsFromRows($rows);
559
    }
560
}
561