Passed
Branch master (6982d9)
by Ivan
02:35
created

NavigationBuilder::addItemsFromStack()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 2
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\NestableInterface;
10
11
/**
12
 * Class NavigationBuilder.
13
 *
14
 * @author Ivan Barlog <[email protected]>
15
 */
16
class NavigationBuilder
17
{
18
    /** @var ContainerInterface[] */
19
    private $container;
20
    /** @var RootNode */
21
    private $root;
22
    /** @var ItemInterface[] */
23
    private $stack = [];
24
    /** @var ItemInterface[] */
25
    private $used = [];
26
    /** @var ItemInterface */
27
    private $current;
28
29
    public function __construct(ContainerInterface $container, MatcherInterface $matcher)
30
    {
31
        $this->container = $container;
0 ignored issues
show
Documentation Bug introduced by
It seems like $container of type Everlution\Navigation\ContainerInterface is incompatible with the declared type Everlution\Navigation\ContainerInterface[] of property $container.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
32
        $this->build($matcher);
33
    }
34
35
    /**
36
     * @return ItemInterface[]
37
     *
38
     * @throws NoCurrentItemFoundException
39
     */
40
    public function getBreadcrumbs(): array
41
    {
42
        if (empty($this->stack)) {
43
            $this->getRootItem($this->getCurrent());
44
            $this->stack = array_reverse($this->stack);
45
            $this->stack = array_map(
46
                function (ItemInterface $item) {
47
                    return $this->used[get_class($item)];
48
                },
49
                $this->stack
50
            );
51
        }
52
53
        return $this->stack;
54
    }
55
56
    /**
57
     * @param ItemInterface $item
58
     *
59
     * @return bool
60
     *
61
     * @throws NoCurrentItemFoundException
62
     */
63
    public function isAncestor(ItemInterface $item): bool
64
    {
65
        $ancestors = $this->getBreadcrumbs();
66
        array_pop($ancestors);
67
68
        return in_array($item, $ancestors);
69
    }
70
71
    /**
72
     * @return ItemInterface
73
     *
74
     * @throws NoCurrentItemFoundException
75
     */
76
    public function getCurrent(): ItemInterface
77
    {
78
        if (!$this->current) {
79
            throw new NoCurrentItemFoundException();
80
        }
81
82
        return $this->current;
83
    }
84
85
    public function getRoot(): RootNode
86
    {
87
        return $this->root;
88
    }
89
90
    private function build(MatcherInterface $matcher)
91
    {
92
        $this->root = new RootNode();
93
        foreach ($this->container->getItems() as $item) {
94
            if (!$this->current && $matcher->isCurrent($item)) {
95
                $this->current = $item;
96
            }
97
98
            if ($item instanceof NestableInterface) {
99
                $this->getRootItem($item);
100
                $this->addItemsFromStack();
101
                continue;
102
            }
103
104
            $this->addItem($this->root, $item);
105
        }
106
    }
107
108
    private function getParent(NestableInterface $item): ItemInterface
109
    {
110
        return $this->container->get($item->getParent());
111
    }
112
113
    private function getRootItem(ItemInterface $item): ?ItemInterface
114
    {
115
        $this->stack[] = $item;
116
117
        if (!$item instanceof NestableInterface) {
118
            return null;
119
        }
120
121
        return $this->getRootItem($this->getParent($item));
122
    }
123
124
    private function addItemsFromStack(): void
125
    {
126
        $root = $this->root;
127
        while ($item = array_pop($this->stack)) {
128
            $this->addItem($root, $item);
129
            $root = $root->get(get_class($item));
130
        }
131
    }
132
133
    private function addItem(RootNode $root, ItemInterface $item): void
134
    {
135
        $name = get_class($item);
136
        if (array_key_exists($name, $this->used)) {
137
            return;
138
        }
139
140
        $parentNode = new ParentNode($item);
141
        $this->used[$name] = $parentNode;
142
        $root->addChild($parentNode);
143
    }
144
}
145