Completed
Push — master ( 6a996d...b41795 )
by Mathieu
13:00 queued 10:14
created

AbstractMenu::addItem()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 26
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 26
rs 8.439
c 0
b 0
f 0
cc 6
eloc 19
nc 9
nop 2
1
<?php
2
3
namespace Charcoal\Ui\Menu;
4
5
use \InvalidArgumentException;
6
7
// Intra-module (`charcoal-ui`) dependencies
8
use \Charcoal\Ui\AbstractUiItem;
9
use \Charcoal\Ui\Menu\MenuInterface;
10
use \Charcoal\Ui\MenuItem\MenuItemInterface;
11
use \Charcoal\Ui\MenuItem\MenuItemBuilder;
12
13
/**
14
 * A Basic Menu
15
 *
16
 * Abstract implementation of {@see \Charcoal\Ui\Menu\MenuInterface}.
17
 */
18
abstract class AbstractMenu extends AbstractUiItem implements
19
    MenuInterface
20
{
21
    /**
22
     * A collection menu items.
23
     *
24
     * @var MenuItemInterface[]
25
     */
26
    private $items = [];
27
28
    /**
29
     * A callback applied to each menu item output by {@see self::items()}.
30
     *
31
     * @var callable
32
     */
33
    private $itemCallback;
34
35
    /**
36
     * Store a menu builder instance.
37
     *
38
     * @var MenuItemBuilder $menuItemBuilder
39
     */
40
    private $menuItemBuilder;
41
42
    /**
43
     * Return a new menu.
44
     *
45
     * @param array|\ArrayAccess $data Class dependencies.
46
     */
47
    public function __construct($data)
48
    {
49
        $this->setMenuItemBuilder($data['menu_item_builder']);
50
    }
51
52
    /**
53
     * @param MenuItemBuilder $menuItemBuilder The Menu Item Builder that will be used to create new items.
54
     * @return AsbtractMenu Chainable
55
     */
56
    public function setMenuItemBuilder(MenuItemBuilder $menuItemBuilder)
57
    {
58
        $this->menuItemBuilder = $menuItemBuilder;
59
        return $this;
60
    }
61
62
    /**
63
     * @param callable $cb The item callback.
64
     * @return AbstractMenu Chainable
65
     */
66
    public function setItemCallback(callable $cb)
67
    {
68
        $this->itemCallback = $cb;
69
        return $this;
70
    }
71
72
    /**
73
     * @param array $items The menu items.
74
     * @return AbstractMenu Chainable
75
     */
76
    public function setItems(array $items)
77
    {
78
        $this->items = [];
79
        foreach ($items as $ident => $item) {
80
            $this->addItem($item, $ident);
81
        }
82
        return $this;
83
    }
84
85
    /**
86
     * @param array|MenuItemInterface $item  A menu item structure or object.
87
     * @param string                  $ident The menu item identifier, if any.
88
     * @throws InvalidArgumentException If the item argument is not a structure or object.
89
     * @return MenuItem Chainable
90
     */
91
    public function addItem($item, $ident = null)
92
    {
93
        if (is_array($item)) {
94
            $item['menu'] = $this;
95
            if (!isset($item['ident'])) {
96
                $item['ident'] = $ident;
97
            }
98
            $i = $this->menuItemBuilder->build($item);
99
            $item = $i;
100
        } elseif ($item instanceof MenuItemInterface) {
101
            if ($item->ident() === null) {
102
                $item->setIdent($ident);
103
            }
104
            $item->setMenu($this);
105
        } else {
106
            throw new InvalidArgumentException(
107
                'Item must be an array of menu item options or a MenuItem object'
108
            );
109
        }
110
        if ($ident === null) {
111
            $this->items[] = $item;
112
        } else {
113
            $this->items[$ident] = $item;
114
        }
115
        return $this;
116
    }
117
118
    /**
119
     * Menu Item generator.
120
     *
121
     * @param callable $itemCallback Optional. Item callback.
122
     * @return MenuItemInterface[]
123
     */
124 View Code Duplication
    public function items(callable $itemCallback = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Coding Style introduced by
items uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
125
    {
126
        $items = $this->items;
127
        uasort($items, [$this, 'sortItemsByPriority']);
128
129
        $itemCallback = isset($itemCallback) ? $itemCallback : $this->itemCallback;
130
        foreach ($items as $item) {
131
            if ($itemCallback) {
132
                $itemCallback($item);
133
            }
134
            $GLOBALS['widget_template'] = $item->template();
135
            yield $item->ident() => $item;
136
            $GLOBALS['widget_template'] = '';
137
        }
138
    }
139
140
    /**
141
     * @return boolean
142
     */
143
    public function hasItems()
144
    {
145
        return (count($this->items) > 0);
146
    }
147
148
    /**
149
     * @return integer
150
     */
151
    public function numItems()
152
    {
153
        return count($this->items);
154
    }
155
}
156