NodeRepository   F
last analyzed

Complexity

Total Complexity 68

Size/Duplication

Total Lines 720
Duplicated Lines 2.64 %

Coupling/Cohesion

Components 2
Dependencies 13

Importance

Changes 0
Metric Value
wmc 68
lcom 2
cbo 13
dl 19
loc 720
rs 2.84
c 0
b 0
f 0

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 19 1
A getMapper() 0 4 1
A getFullNameByUserId() 0 4 1
B prepareNode() 11 42 5
A setApiBasePath() 0 4 1
A getNode() 0 21 3
A getIndexNode() 0 6 1
A deleteNode() 0 5 1
A getReferences() 0 7 1
A getNodes() 0 33 2
A getNodesByIds() 0 33 2
A getWebspaceNode() 0 23 2
A getWebspaceNodes() 0 19 2
A createWebspaceNode() 0 38 3
B getFilteredNodes() 0 46 6
A getParentNode() 0 8 2
B prepareNodesTree() 0 34 6
A getNodesTree() 8 34 4
B loadNodeAndAncestors() 0 43 10
B iterateTiers() 0 32 6
A loadExtensionData() 0 25 1
A saveExtensionData() 0 30 1
A orderAt() 0 15 4
A copyLocale() 0 11 2

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 NodeRepository 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 NodeRepository, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of Sulu.
5
 *
6
 * (c) MASSIVE ART WebServices GmbH
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace Sulu\Bundle\PageBundle\Repository;
13
14
use PHPCR\RepositoryException;
15
use Sulu\Bundle\AdminBundle\UserManager\UserManagerInterface;
16
use Sulu\Bundle\PageBundle\Content\InternalLinksContainer;
17
use Sulu\Component\Content\Compat\StructureInterface;
18
use Sulu\Component\Content\Document\Behavior\SecurityBehavior;
19
use Sulu\Component\Content\Exception\InvalidOrderPositionException;
20
use Sulu\Component\Content\Mapper\ContentMapperInterface;
21
use Sulu\Component\Content\Query\ContentQueryBuilderInterface;
22
use Sulu\Component\Content\Query\ContentQueryExecutorInterface;
23
use Sulu\Component\DocumentManager\Exception\DocumentManagerException;
24
use Sulu\Component\PHPCR\SessionManager\SessionManagerInterface;
25
use Sulu\Component\Rest\Exception\RestException;
26
use Sulu\Component\Security\Authorization\AccessControl\AccessControlManagerInterface;
27
use Sulu\Component\Security\Authorization\SecurityCondition;
28
use Sulu\Component\Webspace\Manager\WebspaceManagerInterface;
29
use Sulu\Component\Webspace\Webspace;
30
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
31
32
/**
33
 * repository for node objects.
34
 *
35
 * @deprecated
36
 */
37
class NodeRepository implements NodeRepositoryInterface
0 ignored issues
show
Deprecated Code introduced by
The interface Sulu\Bundle\PageBundle\R...NodeRepositoryInterface has been deprecated.

This class, trait or interface has been deprecated.

Loading history...
38
{
39
    /**
40
     * @var ContentMapperInterface
41
     */
42
    private $mapper;
43
44
    /**
45
     * @var SessionManagerInterface
46
     */
47
    private $sessionManager;
48
49
    /**
50
     * for returning self link in get action.
51
     *
52
     * @var string
53
     */
54
    private $apiBasePath = '/admin/api/nodes';
55
56
    /**
57
     * @var UserManagerInterface
58
     */
59
    private $userManager;
60
61
    /**
62
     * @var WebspaceManagerInterface
63
     */
64
    private $webspaceManager;
65
66
    /**
67
     * @var ContentQueryBuilderInterface
68
     */
69
    private $queryBuilder;
70
71
    /**
72
     * @var ContentQueryExecutorInterface
73
     */
74
    private $queryExecutor;
75
76
    /**
77
     * @var AccessControlManagerInterface
78
     */
79
    private $accessControlManager;
80
81
    /**
82
     * @var TokenStorageInterface
83
     */
84
    private $tokenStorage;
85
86
    public function __construct(
87
        ContentMapperInterface $mapper,
88
        SessionManagerInterface $sessionManager,
89
        UserManagerInterface $userManager,
90
        WebspaceManagerInterface $webspaceManager,
91
        ContentQueryBuilderInterface $queryBuilder,
92
        ContentQueryExecutorInterface $queryExecutor,
93
        AccessControlManagerInterface $accessControlManager,
94
        TokenStorageInterface $tokenStorage = null
95
    ) {
96
        $this->mapper = $mapper;
97
        $this->sessionManager = $sessionManager;
98
        $this->userManager = $userManager;
99
        $this->webspaceManager = $webspaceManager;
100
        $this->queryBuilder = $queryBuilder;
101
        $this->queryExecutor = $queryExecutor;
102
        $this->accessControlManager = $accessControlManager;
103
        $this->tokenStorage = $tokenStorage;
104
    }
105
106
    /**
107
     * return content mapper.
108
     *
109
     * @return ContentMapperInterface
110
     */
111
    protected function getMapper()
112
    {
113
        return $this->mapper;
114
    }
115
116
    /**
117
     * returns user fullName.
118
     *
119
     * @param int $id userId
120
     *
121
     * @return string
122
     */
123
    protected function getFullNameByUserId($id)
124
    {
125
        return $this->userManager->getFullNameByUserId($id);
126
    }
127
128
    /**
129
     * returns finished Node (with _links and _embedded).
130
     *
131
     * @param StructureInterface $structure
132
     * @param string $webspaceKey
133
     * @param string $languageCode
134
     * @param int $depth
135
     * @param bool $complete
136
     * @param bool $excludeGhosts
137
     * @param string|null $extension
138
     *
139
     * @return array
140
     *
141
     * @deprecated This part should be split into a serialization handler and using the hateoas bundle
142
     */
143
    protected function prepareNode(
144
        StructureInterface $structure,
145
        $webspaceKey,
146
        $languageCode,
147
        $depth = 1,
148
        $complete = true,
149
        $excludeGhosts = false,
150
        $extension = null
151
    ) {
152
        $result = $structure->toArray($complete);
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Component\Content\C...ureInterface::toArray() has been deprecated with message: Use the serializer instead

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...
153
154
        // add default embedded property with empty nodes array
155
        $result['_embedded'] = [];
156
        $result['_embedded']['nodes'] = [];
157
158
        // add api links
159
        $result['_links'] = [
160
            'self' => [
161
                'href' => $this->apiBasePath . '/' . $structure->getUuid() .
162
                    (null !== $extension ? '/' . $extension : ''),
163
            ],
164
            'children' => [
165
                'href' => $this->apiBasePath . '?parentId=' . $structure->getUuid() . '&depth=' . $depth .
166
                    '&webspace=' . $webspaceKey . '&language=' . $languageCode .
167
                    (true === $excludeGhosts ? '&exclude-ghosts=true' : ''),
168
            ],
169
        ];
170
171 View Code Duplication
        if ($this->tokenStorage && ($token = $this->tokenStorage->getToken())) {
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...
172
            $result['_permissions'] = $this->accessControlManager->getUserPermissions(
173
                new SecurityCondition(
174
                    'sulu.webspaces.' . $webspaceKey,
175
                    $languageCode,
176
                    SecurityBehavior::class,
177
                    $structure->getUuid()
178
                ),
179
                $token->getUser()
180
            );
181
        }
182
183
        return $result;
184
    }
185
186
    /**
187
     * @param string $apiBasePath
188
     */
189
    public function setApiBasePath($apiBasePath)
190
    {
191
        $this->apiBasePath = $apiBasePath;
192
    }
193
194
    /**
195
     * {@inheritdoc}
196
     */
197
    public function getNode(
198
        $uuid,
199
        $webspaceKey,
200
        $languageCode,
201
        $breadcrumb = false,
202
        $complete = true,
203
        $loadGhostContent = false
204
    ) {
205
        $structure = $this->getMapper()->load($uuid, $webspaceKey, $languageCode, $loadGhostContent);
206
207
        $result = $this->prepareNode($structure, $webspaceKey, $languageCode, 1, $complete);
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Bundle\PageBundle\R...pository::prepareNode() has been deprecated with message: This part should be split into a serialization handler and using the hateoas bundle

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...
208
        if ($breadcrumb) {
209
            $breadcrumb = $this->getMapper()->loadBreadcrumb($uuid, $languageCode, $webspaceKey);
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Component\Content\M...rface::loadBreadcrumb() has been deprecated.

This method has been deprecated.

Loading history...
210
            $result['breadcrumb'] = [];
211
            foreach ($breadcrumb as $item) {
212
                $result['breadcrumb'][$item->getDepth()] = $item->toArray();
213
            }
214
        }
215
216
        return $result;
217
    }
218
219
    /**
220
     * {@inheritdoc}
221
     */
222
    public function getIndexNode($webspaceKey, $languageCode)
223
    {
224
        $structure = $this->getMapper()->loadStartPage($webspaceKey, $languageCode);
225
226
        return $this->prepareNode($structure, $webspaceKey, $languageCode);
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Bundle\PageBundle\R...pository::prepareNode() has been deprecated with message: This part should be split into a serialization handler and using the hateoas bundle

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...
227
    }
228
229
    /**
230
     * {@inheritdoc}
231
     */
232
    public function deleteNode($uuid, $webspaceKey)
233
    {
234
        // TODO remove third parameter, and ask in UI if referenced node should be deleted
235
        $this->getMapper()->delete($uuid, $webspaceKey, true);
0 ignored issues
show
Unused Code introduced by
The call to ContentMapperInterface::delete() has too many arguments starting with true.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
236
    }
237
238
    /**
239
     * {@inheritdoc}
240
     */
241
    public function getReferences($uuid)
242
    {
243
        $session = $this->sessionManager->getSession();
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Component\PHPCR\Ses...Interface::getSession() has been deprecated with message: Use the doctrine_phpcr service to retrieve the session

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...
244
        $node = $session->getNodeByIdentifier($uuid);
245
246
        return iterator_to_array($node->getReferences());
247
    }
248
249
    /**
250
     * {@inheritdoc}
251
     */
252
    public function getNodes(
253
        $parent,
254
        $webspaceKey,
255
        $languageCode,
256
        $depth = 1,
257
        $flat = true,
258
        $complete = true,
259
        $excludeGhosts = false
260
    ) {
261
        $nodes = $this->getMapper()->loadByParent(
262
            $parent,
263
            $webspaceKey,
264
            $languageCode,
265
            $depth,
266
            $flat,
267
            false,
268
            $excludeGhosts
269
        );
270
271
        $parentNode = $this->getParentNode($parent, $webspaceKey, $languageCode);
272
        $result = $this->prepareNode($parentNode, $webspaceKey, $languageCode, 1, $complete, $excludeGhosts);
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Bundle\PageBundle\R...pository::prepareNode() has been deprecated with message: This part should be split into a serialization handler and using the hateoas bundle

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...
273
        $result['_embedded']['nodes'] = $this->prepareNodesTree(
274
            $nodes,
275
            $webspaceKey,
276
            $languageCode,
277
            $complete,
278
            $excludeGhosts,
279
            $flat ? 1 : $depth
280
        );
281
        $result['total'] = count($result['_embedded']['nodes']);
282
283
        return $result;
284
    }
285
286
    /**
287
     * {@inheritdoc}
288
     */
289
    public function getNodesByIds(
290
        $ids,
291
        $webspaceKey,
292
        $languageCode
293
    ) {
294
        $result = [];
295
        $idString = '';
296
297
        if (!empty($ids)) {
298
            $container = new InternalLinksContainer(
299
                $ids,
300
                $this->queryExecutor,
301
                $this->queryBuilder,
302
                [],
303
                $webspaceKey,
304
                $languageCode,
305
                true
306
            );
307
308
            $result = $container->getData();
309
            $idString = implode(',', $ids);
310
        }
311
312
        return [
313
            '_embedded' => [
314
                'nodes' => $result,
315
            ],
316
            'total' => count($result),
317
            '_links' => [
318
                'self' => ['href' => $this->apiBasePath . '?ids=' . $idString],
319
            ],
320
        ];
321
    }
322
323
    /**
324
     * {@inheritdoc}
325
     */
326
    public function getWebspaceNode(
327
        $webspaceKey,
328
        $languageCode,
329
        $depth = 1,
330
        $excludeGhosts = false
331
    ) {
332
        // init result
333
        $data = [];
334
335
        // add default empty embedded property
336
        $data['_embedded'] = [
337
            'nodes' => [$this->createWebspaceNode($webspaceKey, $languageCode, $depth, $excludeGhosts)],
338
        ];
339
        // add api links
340
        $data['_links'] = [
341
            'self' => [
342
                'href' => $this->apiBasePath . '/entry?depth=' . $depth . '&webspace=' . $webspaceKey .
343
                    '&language=' . $languageCode . (true === $excludeGhosts ? '&exclude-ghosts=true' : ''),
344
            ],
345
        ];
346
347
        return $data;
348
    }
349
350
    /**
351
     * {@inheritdoc}
352
     */
353
    public function getWebspaceNodes($languageCode)
354
    {
355
        // init result
356
        $data = ['_embedded' => ['nodes' => []]];
357
358
        /** @var Webspace $webspace */
359
        foreach ($this->webspaceManager->getWebspaceCollection() as $webspace) {
360
            $data['_embedded']['nodes'][] = $this->createWebspaceNode($webspace->getKey(), $languageCode, 0);
361
        }
362
363
        // add api links
364
        $data['_links'] = [
365
            'self' => [
366
                'href' => $this->apiBasePath . '/entry?language=' . $languageCode,
367
            ],
368
        ];
369
370
        return $data;
371
    }
372
373
    /**
374
     * Creates a webspace node.
375
     */
376
    private function createWebspaceNode(
377
        $webspaceKey,
378
        $languageCode,
379
        $depth = 1,
380
        $excludeGhosts = false
381
    ) {
382
        $webspace = $this->webspaceManager->getWebspaceCollection()->getWebspace($webspaceKey);
383
384
        if ($depth > 0) {
385
            $nodes = $this->getMapper()->loadByParent(
386
                null,
387
                $webspaceKey,
388
                $languageCode,
389
                $depth,
390
                false,
391
                false,
392
                $excludeGhosts
393
            );
394
            $embedded = $this->prepareNodesTree($nodes, $webspaceKey, $languageCode, true, $excludeGhosts, $depth);
395
        } else {
396
            $embedded = [];
397
        }
398
399
        return [
400
            'id' => $this->sessionManager->getContentNode($webspace->getKey())->getIdentifier(),
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Component\PHPCR\Ses...rface::getContentNode() has been deprecated with message: Do not use anymore, because the node is always returned from the default session, although multiple sessions can exist

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...
401
            'path' => '/',
402
            'title' => $webspace->getName(),
403
            'hasSub' => true,
404
            'publishedState' => true,
405
            '_embedded' => $embedded,
406
            '_links' => [
407
                'children' => [
408
                    'href' => $this->apiBasePath . '?depth=' . $depth . '&webspace=' . $webspaceKey .
409
                        '&language=' . $languageCode . (true === $excludeGhosts ? '&exclude-ghosts=true' : ''),
410
                ],
411
            ],
412
        ];
413
    }
414
415
    /**
416
     * {@inheritdoc}
417
     */
418
    public function getFilteredNodes(
419
        array $filterConfig,
420
        $languageCode,
421
        $webspaceKey,
422
        $preview = false,
423
        $api = false,
424
        $exclude = []
425
    ) {
426
        $limit = isset($filterConfig['limitResult']) ? $filterConfig['limitResult'] : null;
427
        $initParams = ['config' => $filterConfig];
428
        if ($exclude) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $exclude of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
429
            $initParams['excluded'] = $exclude;
430
        }
431
432
        $this->queryBuilder->init($initParams);
433
        $data = $this->queryExecutor->execute(
434
            $webspaceKey,
435
            [$languageCode],
436
            $this->queryBuilder,
437
            true,
438
            -1,
439
            $limit
440
        );
441
442
        if ($api) {
443
            if (isset($filterConfig['dataSource'])) {
444
                if (null !== $this->webspaceManager->findWebspaceByKey($filterConfig['dataSource'])) {
445
                    $node = $this->sessionManager->getContentNode($filterConfig['dataSource']);
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Component\PHPCR\Ses...rface::getContentNode() has been deprecated with message: Do not use anymore, because the node is always returned from the default session, although multiple sessions can exist

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...
446
                } else {
447
                    $node = $this->sessionManager->getSession()->getNodeByIdentifier($filterConfig['dataSource']);
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Component\PHPCR\Ses...Interface::getSession() has been deprecated with message: Use the doctrine_phpcr service to retrieve the session

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...
448
                }
449
            } else {
450
                $node = $this->sessionManager->getContentNode($webspaceKey);
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Component\PHPCR\Ses...rface::getContentNode() has been deprecated with message: Do not use anymore, because the node is always returned from the default session, although multiple sessions can exist

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...
451
            }
452
453
            $parentNode = $this->getParentNode($node->getIdentifier(), $webspaceKey, $languageCode);
454
            $result = $this->prepareNode($parentNode, $webspaceKey, $languageCode, 1, false);
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Bundle\PageBundle\R...pository::prepareNode() has been deprecated with message: This part should be split into a serialization handler and using the hateoas bundle

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...
455
456
            $result['_embedded']['nodes'] = $data;
457
            $result['total'] = count($result['_embedded']['nodes']);
458
        } else {
459
            $result = $data;
460
        }
461
462
        return $result;
463
    }
464
465
    /**
466
     * if parent is null return home page else the page with given uuid.
467
     *
468
     * @param string|null $parent uuid of parent node
469
     * @param string $webspaceKey
470
     * @param string $languageCode
471
     *
472
     * @return StructureInterface
473
     */
474
    private function getParentNode($parent, $webspaceKey, $languageCode)
475
    {
476
        if (null != $parent) {
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $parent of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison !== instead.
Loading history...
477
            return $this->getMapper()->load($parent, $webspaceKey, $languageCode);
478
        } else {
479
            return $this->getMapper()->loadStartPage($webspaceKey, $languageCode);
480
        }
481
    }
482
483
    /**
484
     * @param StructureInterface[] $nodes
485
     * @param string $webspaceKey
486
     * @param string $languageCode
487
     * @param bool $complete
488
     * @param bool $excludeGhosts
489
     *
490
     * @return array
491
     */
492
    private function prepareNodesTree(
493
        $nodes,
494
        $webspaceKey,
495
        $languageCode,
496
        $complete = true,
497
        $excludeGhosts = false,
498
        $maxDepth = 1,
499
        $currentDepth = 0
500
    ) {
501
        ++$currentDepth;
502
503
        $results = [];
504
        foreach ($nodes as $node) {
505
            $result = $this->prepareNode($node, $webspaceKey, $languageCode, 1, $complete, $excludeGhosts);
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Bundle\PageBundle\R...pository::prepareNode() has been deprecated with message: This part should be split into a serialization handler and using the hateoas bundle

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...
506
            if (null !== $maxDepth &&
507
                $currentDepth < $maxDepth &&
508
                $node->getHasChildren() &&
509
                null != $node->getChildren()
510
            ) {
511
                $result['_embedded']['nodes'] = $this->prepareNodesTree(
512
                    $node->getChildren(),
513
                    $webspaceKey,
514
                    $languageCode,
515
                    $complete,
516
                    $excludeGhosts,
517
                    $maxDepth,
518
                    $currentDepth
519
                );
520
            }
521
            $results[] = $result;
522
        }
523
524
        return $results;
525
    }
526
527
    /**
528
     * {@inheritdoc}
529
     */
530
    public function getNodesTree(
531
        $uuid,
532
        $webspaceKey,
533
        $languageCode,
534
        $excludeGhosts = false,
535
        $excludeShadows = false
536
    ) {
537
        $nodes = $this->loadNodeAndAncestors($uuid, $webspaceKey, $languageCode, $excludeGhosts, $excludeShadows, true);
538
539
        $result = [
540
            '_embedded' => [
541
                'nodes' => $nodes,
542
            ],
543
        ];
544
545 View Code Duplication
        if ($this->tokenStorage && ($token = $this->tokenStorage->getToken())) {
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...
546
            $result['_permissions'] = $this->accessControlManager->getUserPermissions(
547
                new SecurityCondition(
548
                    'sulu.webspaces.' . $webspaceKey
549
                ),
550
                $token->getUser()
551
            );
552
        }
553
554
        // add api links
555
        $result['_links'] = [
556
            'self' => [
557
                'href' => $this->apiBasePath . '/tree?uuid=' . $uuid . '&webspace=' . $webspaceKey . '&language=' .
558
                    $languageCode . (true === $excludeGhosts ? '&exclude-ghosts=true' : ''),
559
            ],
560
        ];
561
562
        return $result;
563
    }
564
565
    /**
566
     * Load the node and its ancestors and convert them into a HATEOAS representation.
567
     *
568
     * @param string $uuid
569
     * @param string $webspaceKey
570
     * @param string $locale
571
     * @param bool $excludeGhosts
572
     * @param bool $excludeShadows
573
     * @param bool $complete
574
     *
575
     * @return array
576
     */
577
    private function loadNodeAndAncestors($uuid, $webspaceKey, $locale, $excludeGhosts, $excludeShadows, $complete)
578
    {
579
        $descendants = $this->getMapper()->loadNodeAndAncestors(
580
            $uuid,
581
            $locale,
582
            $webspaceKey,
583
            $excludeGhosts,
584
            $excludeShadows
585
        );
586
        $descendants = array_reverse($descendants);
587
588
        $childTiers = [];
589
        foreach ($descendants as $descendant) {
590
            foreach ($descendant->getChildren() as $child) {
591
                $type = $child->getType();
592
593
                if ($excludeShadows && null !== $type && 'shadow' === $type->getName()) {
594
                    continue;
595
                }
596
597
                if ($excludeGhosts && null !== $type && 'ghost' === $type->getName()) {
598
                    continue;
599
                }
600
601
                if (!isset($childTiers[$descendant->getUuid()])) {
602
                    $childTiers[$descendant->getUuid()] = [];
603
                }
604
                $childTiers[$descendant->getUuid()][] = $this->prepareNode(
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Bundle\PageBundle\R...pository::prepareNode() has been deprecated with message: This part should be split into a serialization handler and using the hateoas bundle

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...
605
                    $child,
606
                    $webspaceKey,
607
                    $locale,
608
                    1,
609
                    $complete,
610
                    $excludeGhosts
611
                );
612
            }
613
        }
614
        $result = array_shift($childTiers);
615
616
        $this->iterateTiers($childTiers, $result);
617
618
        return $result;
619
    }
620
621
    /**
622
     * Iterate over the ancestor tiers and build up the result.
623
     *
624
     * @param array $tiers
625
     * @param array $result (by rereference)
626
     */
627
    private function iterateTiers($tiers, &$result)
628
    {
629
        reset($tiers);
630
        $uuid = key($tiers);
631
        $tier = array_shift($tiers);
632
633
        $found = false;
634
        if (is_array($result)) {
635
            foreach ($result as &$node) {
636
                if ($node['id'] === $uuid) {
637
                    $node['_embedded']['nodes'] = $tier;
638
                    $found = true;
639
                    break;
640
                }
641
            }
642
        }
643
644
        if (!$tiers) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $tiers of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
645
            return;
646
        }
647
648
        if (!$found) {
649
            throw new \RuntimeException(
650
                sprintf(
651
                    'Could not find target node in with UUID "%s" in tier. This should not happen.',
652
                    $uuid
653
                )
654
            );
655
        }
656
657
        $this->iterateTiers($tiers, $node['_embedded']['nodes']);
0 ignored issues
show
Bug introduced by
The variable $node seems to be defined by a foreach iteration on line 635. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
658
    }
659
660
    /**
661
     * {@inheritdoc}
662
     */
663
    public function loadExtensionData($uuid, $extensionName, $webspaceKey, $languageCode)
664
    {
665
        $structure = $this->getMapper()->load($uuid, $webspaceKey, $languageCode);
666
667
        // extract extension
668
        $extensionData = $structure->getExt();
669
        $data = $extensionData[$extensionName];
670
671
        // add uuid and path
672
        $data['id'] = $structure->getUuid();
673
        $data['path'] = $structure->getPath();
674
        $data['url'] = $structure->getResourceLocator();
675
        $data['publishedState'] = $structure->getPublishedState();
676
        $data['published'] = $structure->getPublished();
677
678
        // prepare data
679
        $data['_links'] = [
680
            'self' => [
681
                'href' => $this->apiBasePath . '/' . $uuid . '/' . $extensionName . '?webspace=' . $webspaceKey .
682
                    '&language=' . $languageCode,
683
            ],
684
        ];
685
686
        return $data;
687
    }
688
689
    /**
690
     * {@inheritdoc}
691
     */
692
    public function saveExtensionData($uuid, $data, $extensionName, $webspaceKey, $languageCode, $userId)
693
    {
694
        $structure = $this->getMapper()->saveExtension(
695
            $uuid,
696
            $data,
697
            $extensionName,
698
            $webspaceKey,
699
            $languageCode,
700
            $userId
701
        );
702
703
        // extract extension
704
        $extensionData = $structure->getExt();
705
        $data = $extensionData[$extensionName];
706
707
        // add uuid and path
708
        $data['id'] = $structure->getUuid();
709
        $data['path'] = $structure->getPath();
710
        $data['url'] = $structure->getResourceLocator();
711
712
        // prepare data
713
        $data['_links'] = [
714
            'self' => [
715
                'href' => $this->apiBasePath . '/' . $uuid . '/' . $extensionName . '?webspace=' . $webspaceKey .
716
                    '&language=' . $languageCode,
717
            ],
718
        ];
719
720
        return $data;
721
    }
722
723
    /**
724
     * {@inheritdoc}
725
     */
726
    public function orderAt($uuid, $position, $webspaceKey, $languageCode, $userId)
727
    {
728
        try {
729
            // call mapper function
730
            $structure = $this->getMapper()->orderAt($uuid, $position, $userId, $webspaceKey, $languageCode);
731
        } catch (DocumentManagerException $ex) {
732
            throw new RestException($ex->getMessage(), 1, $ex);
733
        } catch (RepositoryException $ex) {
0 ignored issues
show
Bug introduced by
The class PHPCR\RepositoryException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
734
            throw new RestException($ex->getMessage(), 1, $ex);
735
        } catch (InvalidOrderPositionException $ex) {
736
            throw new RestException($ex->getMessage(), 1, $ex);
737
        }
738
739
        return $this->prepareNode($structure, $webspaceKey, $languageCode);
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Bundle\PageBundle\R...pository::prepareNode() has been deprecated with message: This part should be split into a serialization handler and using the hateoas bundle

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...
740
    }
741
742
    /**
743
     * {@inheritdoc}
744
     */
745
    public function copyLocale($uuid, $userId, $webspaceKey, $srcLocale, $destLocales)
746
    {
747
        try {
748
            // call mapper function
749
            $structure = $this->getMapper()->copyLanguage($uuid, $userId, $webspaceKey, $srcLocale, $destLocales);
750
        } catch (RepositoryException $ex) {
0 ignored issues
show
Bug introduced by
The class PHPCR\RepositoryException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
751
            throw new RestException($ex->getMessage(), 1, $ex);
752
        }
753
754
        return $this->prepareNode($structure, $webspaceKey, $srcLocale);
0 ignored issues
show
Deprecated Code introduced by
The method Sulu\Bundle\PageBundle\R...pository::prepareNode() has been deprecated with message: This part should be split into a serialization handler and using the hateoas bundle

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...
755
    }
756
}
757