Passed
Pull Request — master (#8)
by
unknown
02:32
created

NavigationBuilder::getCurrentRoot()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 4
nc 2
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Everlution\Navigation\Builder;
6
7
use Everlution\Navigation\ContainerInterface;
8
use Everlution\Navigation\Item\ItemInterface;
9
use Everlution\Navigation\Item\SplittableInterface;
0 ignored issues
show
Bug introduced by
The type Everlution\Navigation\Item\SplittableInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
10
use Everlution\Navigation\Item\NestableInterface;
11
use Everlution\Navigation\OrderedContainer;
12
13
/**
14
 * Class NavigationBuilder.
15
 *
16
 * @author Ivan Barlog <[email protected]>
17
 */
18
class NavigationBuilder
19
{
20
    /** @var ContainerInterface */
21
    private $container;
22
    /** @var RootNode */
23
    private $root;
24
    /** @var ItemInterface[] */
25
    private $stack = [];
26
    /** @var ParentNode[] */
27
    private $used = [];
28
    /** @var ParentNode */
29
    private $current;
30
    /** @var MatcherInterface */
31
    private $matcher;
32
33
    public function __construct(ContainerInterface $container, MatcherInterface $matcher)
34
    {
35
        $this->container = new OrderedContainer($container);
36
        $this->matcher = $matcher;
37
        $this->build();
38
    }
39
40
    /**
41
     * @return ItemInterface[]
42
     *
43
     * @throws NoCurrentItemFoundException
44
     */
45
    public function getBreadcrumbs(): array
46
    {
47
        if (empty($this->stack)) {
48
            $this->getRootItem($this->getCurrent());
49
            $this->stack = array_reverse($this->stack);
50
            $this->stack = array_map(
51
                function (ItemInterface $item) {
52
                    return $this->used[get_class($item)];
53
                },
54
                $this->stack
55
            );
56
        }
57
58
        return $this->stack;
59
    }
60
61
    /**
62
     * @param ItemInterface $item
63
     *
64
     * @return bool
65
     *
66
     * @throws NoCurrentItemFoundException
67
     */
68
    public function isAncestor(ItemInterface $item): bool
69
    {
70
        $ancestors = $this->getBreadcrumbs();
71
        array_pop($ancestors);
72
73
        return in_array(
74
            $item,
75
            array_map(
76
                function (ParentNode $node) {
77
                    return $node->getItem();
78
                },
79
                $ancestors
80
            )
81
        );
82
    }
83
84
    /**
85
     * @return ItemInterface
86
     *
87
     * @throws NoCurrentItemFoundException
88
     */
89
    public function getCurrent(): ItemInterface
90
    {
91
        if (!$this->current) {
92
            throw new NoCurrentItemFoundException();
93
        }
94
95
        return $this->current->getItem();
96
    }
97
98
    public function getCurrentNode(): ParentNode
99
    {
100
        if (!$this->current) {
101
            throw new NoCurrentItemFoundException();
102
        }
103
104
        return $this->current;
105
    }
106
107
    public function getCurrentRoot()
108
    {
109
        $root = $this->getCurrentNode();
110
        while ($root instanceof ParentNode and false === ($root->getItem() instanceof SplittableInterface)) {
111
            $root = $root->getParent();
112
        }
113
114
        return $root;
115
    }
116
117
    public function getRoot(): RootNode
118
    {
119
        return $this->root;
120
    }
121
122
    private function build()
123
    {
124
        $this->root = new RootNode();
125
        foreach ($this->container->getItems() as $item) {
126
            if ($item instanceof NestableInterface) {
127
                $this->getRootItem($item);
128
                $this->addItemsFromStack();
129
                continue;
130
            }
131
132
            $this->addItem($this->root, $item);
133
        }
134
    }
135
136
    private function getParent(NestableInterface $item): ItemInterface
137
    {
138
        return $this->container->get($item->getParent());
139
    }
140
141
    private function getRootItem(ItemInterface $item): ?ItemInterface
142
    {
143
        $this->stack[] = $item;
144
145
        if (!$item instanceof NestableInterface) {
146
            return null;
147
        }
148
149
        return $this->getRootItem($this->getParent($item));
150
    }
151
152
    private function addItemsFromStack(): void
153
    {
154
        $root = $this->root;
155
        while ($item = array_pop($this->stack)) {
156
            $this->addItem($root, $item);
157
            $root = $root->get(get_class($item));
158
        }
159
    }
160
161
    private function addItem(RootNode $root, ItemInterface $item): void
162
    {
163
        $name = get_class($item);
164
        if (array_key_exists($name, $this->used)) {
165
            return;
166
        }
167
168
        $parentNode = new ParentNode($item, $root);
169
        $this->used[$name] = $parentNode;
170
        $root->addChild($parentNode);
171
        $this->setCurrentItem($parentNode);
172
    }
173
174
    private function setCurrentItem(ParentNode $item): void
175
    {
176
        if (!$this->current && $this->matcher->isCurrent($item->getItem())) {
177
            $this->current = $item;
178
        }
179
    }
180
}
181