Passed
Push — master ( 909177...0b3974 )
by Ivan
04:41
created

NavigationBuilder::getCurrentNode()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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