Completed
Pull Request — master (#1)
by David
01:49
created

MenuItem::isActive()   C

Complexity

Conditions 7
Paths 10

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 16
nc 10
nop 1
dl 0
loc 27
rs 6.7272
c 0
b 0
f 0
1
<?php
2
namespace TheCodingMachine\CMS\StaticRegistry\Menu;
3
4
/**
5
 * This class represent a menu item.
6
 */
7
class MenuItem {
8
9
    /**
10
     * The text for the menu item
11
     *
12
     * @var string
13
     */
14
    private $label;
15
16
    /**
17
     * The link for the menu (relative to the root url), unless it starts with / or http:// or https:// or # or ?.
18
     *
19
     * @var string|null
20
     */
21
    private $url;
22
23
    /**
24
     * The children menu item of this menu (if any).
25
     *
26
     * @var MenuItem[]
27
     */
28
    private $children;
29
30
    /**
31
     * The CSS class for the menu, if any.
32
     *
33
     * @var string
34
     */
35
    private $cssClass;
36
37
    /**
38
     * Whether the menu is in an active state or not.
39
     *
40
     * @var bool
41
     */
42
    private $isActive;
43
44
    /**
45
     * Whether the menu is extended or not.
46
     * This should not have an effect if the menu has no child.
47
     *
48
     * @var bool
49
     */
50
    private $isExtended;
51
52
    /**
53
     * Level of priority used to order the menu items.
54
     *
55
     * @var float
56
     */
57
    private $priority = 0.0;
58
59
    /**
60
     * @var bool
61
     */
62
    private $activateBasedOnUrl;
63
64
    /**
65
     * @var bool
66
     */
67
    private $sorted = false;
68
69
    /**
70
     * @param string $label The text for the menu item
71
     * @param string|null $url The link for the menu (relative to the root url), unless it starts with / or http:// or https:// or # or ?.
72
     * @param MenuItem[] $children
73
     */
74
    public function __construct(string $label, string $url=null, array $children=[]) {
75
        $this->label = $label;
76
        $this->url = $url;
77
        $this->children = $children;
78
    }
79
80
    /**
81
     * Returns the label for the menu item.
82
     * @return string
83
     */
84
    public function getLabel(): string {
85
        return $this->label;
86
    }
87
88
    /**
89
     * Returns the URL for this menu (or null if this menu is not a link).
90
     * @return string|null
91
     */
92
    public function getUrl(): ?string {
93
        return $this->url;
94
    }
95
96
    public function findChild(string $label): ?MenuItem
97
    {
98
        foreach ($this->children as $child) {
99
            if ($child->getLabel() === $label) {
100
                return $child;
101
            }
102
        }
103
        return null;
104
    }
105
106
    /**
107
     * Returns a list of children elements for the menu (if there are some).
108
     *
109
     * @return MenuItem[]
110
     */
111
    public function getChildren(): array {
112
        if ($this->sorted === false && $this->children) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->children of type TheCodingMachine\CMS\Sta...egistry\Menu\MenuItem[] 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...
113
            // First, let's make 2 arrays: the array of children with a priority, and the array without.
114
            $childrenWithPriorities = array();
115
            $childrenWithoutPriorities = array();
116
            foreach ($this->children as $child) {
117
                /* @var $child MenuItem */
118
                $priority = $child->getPriority();
119
                if ($priority === null) {
120
                    $childrenWithoutPriorities[] = $child;
121
                } else {
122
                    $childrenWithPriorities[] = $child;
123
                }
124
            }
125
126
            usort($childrenWithPriorities, [$this, 'compareMenuItems']);
127
            $this->children = array_merge($childrenWithPriorities, $childrenWithoutPriorities);
128
            $this->sorted = true;
129
        }
130
        return $this->children;
131
    }
132
133
    public function compareMenuItems(MenuItem $item1, MenuItem $item2): int {
134
        return $item1->getPriority() <=> $item2->getPriority();
135
    }
136
137
    /**
138
     * The children menu item of this menu (if any).
139
     *
140
     * @param MenuItem[] $children
141
     * @return MenuItem
142
     */
143
    public function setChildren(array $children): self {
144
        $this->sorted = false;
145
        $this->children = $children;
146
        return $this;
147
    }
148
149
    /**
150
     * Adds a menu item as a child of this menu item.
151
     *
152
     * @param MenuItem $menuItem
153
     * @return MenuItem
154
     */
155
    public function addMenuItem(MenuItem $menuItem): self {
156
        $this->sorted = false;
157
        $this->children[] = $menuItem;
158
        return $this;
159
    }
160
161
    /**
162
     * Returns true if the menu is in active state (if we are on the page for this menu).
163
     * @return bool
164
     */
165
    public function isActive(string $rootUrl) {
166
        if ($this->isActive) {
167
            return true;
168
        }
169
170
        if($this->activateBasedOnUrl && $this->url !== null) {
171
            $urlParts = parse_url($_SERVER['REQUEST_URI']);
172
            $menuUrlParts = parse_url($this->getLink($rootUrl));
173
174
            if (isset($menuUrlParts['path'])) {
175
                $menuUrl = $menuUrlParts['path'];
176
            } else {
177
                $menuUrl = '/';
178
            }
179
180
            if (isset($urlParts['path'])) {
181
                $requestUrl = $urlParts['path'];
182
            } else {
183
                $requestUrl = '/';
184
            }
185
186
            if($requestUrl === $menuUrl) {
187
                return true;
188
            }
189
        }
190
191
        return false;
192
    }
193
194
    /**
195
     * Set the active state of the menu.
196
     *
197
     * @param bool $isActive
198
     * @return MenuItem
199
     */
200
    public function setIsActive(bool $isActive): self {
201
        $this->isActive = $isActive;
202
        return $this;
203
    }
204
205
    /**
206
     * Enables the menu item (activates it).
207
     *
208
     */
209
    public function enable(): self {
210
        $this->isActive = true;
211
        return $this;
212
    }
213
214
    /**
215
     * Returns true if the menu should be in extended state (if we can see the children directly).
216
     * @return bool
217
     */
218
    public function isExtended(): bool {
219
        // TODO: is extended if one of the sub menus is active!
220
        return $this->isExtended;
221
    }
222
223
    /**
224
     * Whether the menu is extended or not.
225
     * This should not have an effect if the menu has no child.
226
     *
227
     * @param bool $isExtended
228
     * @return MenuItem
229
     */
230
    public function setIsExtended(bool $isExtended = true): self {
231
        $this->isExtended = $isExtended;
232
        return $this;
233
    }
234
235
    /**
236
     * Returns an optional CSS class to apply to the menu item.
237
     * @return string|null
238
     */
239
    public function getCssClass(): ?string {
240
        return $this->cssClass;
241
    }
242
243
    /**
244
     * An optional CSS class to apply to the menu item.
245
     * Use of this property depends on the menu implementation.
246
     *
247
     * @param string $cssClass
248
     */
249
    public function setCssClass($cssClass) {
250
        $this->cssClass = $cssClass;
251
        return $this;
252
    }
253
254
    /**
255
     * Level of priority used to order the menu items.
256
     *
257
     * @param float $priority
258
     */
259
    public function setPriority(float $priority) {
260
        $this->priority = $priority;
261
        return $this;
262
    }
263
264
    /**
265
     * Returns the level of priority. It is used to order the menu items.
266
     * @return float
267
     */
268
    public function getPriority(): float {
269
        return $this->priority;
270
    }
271
272
    /**
273
     * Returns the absolute URL, with parameters if required.
274
     * @return string
275
     */
276
    public function getLink(string $rootUrl) {
277
        if ($this->url === null) {
278
            return null;
279
        }
280
281
        if (strpos($this->url, "/") === 0
282
            || strpos($this->url, "javascript:") === 0
283
            || strpos($this->url, "http://") === 0
284
            || strpos($this->url, "https://") === 0
285
            || strpos($this->url, "?") === 0
286
            || strpos($this->url, "#") === 0) {
287
            return $this->url;
288
        }
289
290
        return $rootUrl.$this->url;
291
    }
292
293
    /**
294
     * If the URL of the current page matches the URL of the link, the link will be considered as "active".
295
     *
296
     * @param bool $activateBasedOnUrl
297
     * @return MenuItem
298
     */
299
    public function setActivateBasedOnUrl(bool $activateBasedOnUrl = true): self {
300
        $this->activateBasedOnUrl = $activateBasedOnUrl;
301
        return $this;
302
    }
303
}
304