Passed
Push — master ( fc2b6f...c8720f )
by Ivan
01:45
created

NavigationBuilder::setCurrentItem()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 3
eloc 2
nc 2
nop 2
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;
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
            $this->setCurrentItem($matcher, $item);
95
96
            if ($item instanceof NestableInterface) {
97
                $this->getRootItem($item);
98
                $this->addItemsFromStack();
99
                continue;
100
            }
101
102
            $this->addItem($this->root, $item);
103
        }
104
    }
105
106
    private function getParent(NestableInterface $item): ItemInterface
107
    {
108
        return $this->container->get($item->getParent());
109
    }
110
111
    private function getRootItem(ItemInterface $item): ?ItemInterface
112
    {
113
        $this->stack[] = $item;
114
115
        if (!$item instanceof NestableInterface) {
116
            return null;
117
        }
118
119
        return $this->getRootItem($this->getParent($item));
120
    }
121
122
    private function addItemsFromStack(): void
123
    {
124
        $root = $this->root;
125
        while ($item = array_pop($this->stack)) {
126
            $this->addItem($root, $item);
127
            $root = $root->get(get_class($item));
128
        }
129
    }
130
131
    private function addItem(RootNode $root, ItemInterface $item): void
132
    {
133
        $name = get_class($item);
134
        if (array_key_exists($name, $this->used)) {
135
            return;
136
        }
137
138
        $parentNode = new ParentNode($item);
139
        $this->used[$name] = $parentNode;
140
        $root->addChild($parentNode);
141
    }
142
143
    private function setCurrentItem(MatcherInterface $matcher, ItemInterface $item): void
144
    {
145
        if (!$this->current && $matcher->isCurrent($item)) {
146
            $this->current = $item;
147
        }
148
    }
149
}
150