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

DashboardTrait::sortWidgetsByPriority()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 7
Ratio 100 %

Importance

Changes 0
Metric Value
dl 7
loc 7
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 2
1
<?php
2
3
namespace Charcoal\Ui\Dashboard;
4
5
use InvalidArgumentException;
6
7
// From 'charcoal-user'
8
use Charcoal\User\AuthAwareInterface;
9
10
// From 'charcoal-ui'
11
use Charcoal\Ui\UiItemInterface;
12
13
/**
14
 * Provides an implementation of {@see \Charcoal\Ui\Dashboard\DashboardInterface}.
15
 */
16
trait DashboardTrait
17
{
18
    /**
19
     * A colletion of widgets.
20
     *
21
     * @var UiItemInterface[]
22
     */
23
    private $widgets;
24
25
    /**
26
     * Store a widget builder instance.
27
     *
28
     * @var object
29
     */
30
    protected $widgetBuilder;
31
32
    /**
33
     * A callback applied to each widget output by {@see self::widgets()}.
34
     *
35
     * @var callable
36
     */
37
    private $widgetCallback;
38
39
    /**
40
     * Set a widget builder.
41
     *
42
     * @param  object $builder The builder to create customized widget objects.
43
     * @throws InvalidArgumentException If the argument is not a widget builder.
44
     * @return DashboardInterface Chainable
45
     */
46
    protected function setWidgetBuilder($builder)
47
    {
48 View Code Duplication
        if (is_object($builder)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
49
            $this->widgetBuilder = $builder;
50
        } else {
51
            throw new InvalidArgumentException(
52
                sprintf(
53
                    'Argument must be a widget builder, %s given',
54
                    (is_object($builder) ? get_class($builder) : gettype($builder))
55
                )
56
            );
57
        }
58
59
        return $this;
60
    }
61
62
    /**
63
     * Set a callback to be applied to each widget output by {@see self::widgets()}.
64
     *
65
     * @param  callable|null $callable A callback to be applied to each widget
66
     *     or NULL to disable the callback.
67
     * @throws InvalidArgumentException If the argument is not callable or NULL.
68
     * @return DashboardInterface Chainable
69
     */
70
    public function setWidgetCallback($callable)
71
    {
72
        if ($callable === null) {
73
            $this->widgetCallback = null;
74
75
            return $this;
76
        }
77
78 View Code Duplication
        if (is_callable($callable)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
79
            $this->widgetCallback = $callable;
80
        } else {
81
            throw new InvalidArgumentException(
82
                sprintf(
83
                    'Argument must be callable or NULL, %s given',
84
                    (is_object($callable) ? get_class($callable) : gettype($callable))
85
                )
86
            );
87
        }
88
89
        return $this;
90
    }
91
92
    /**
93
     * Set the dashboard's widgets.
94
     *
95
     * @param array $widgets A collection of widgets.
96
     * @return DashboardInterface Chainable
97
     */
98
    public function setWidgets(array $widgets)
99
    {
100
        $this->widgets = [];
101
102
        foreach ($widgets as $widgetIdent => $widget) {
103
            $this->addWidget($widgetIdent, $widget);
104
        }
105
106
        return $this;
107
    }
108
109
    /**
110
     * Add a widget to the dashboard.
111
     *
112
     * If a widget with the same $widgetIdent already exists, it will be overridden.
113
     *
114
     * @param  string                $widgetIdent The widget identifier.
115
     * @param  UiItemInterface|array $widget      The widget object or structure.
116
     * @throws InvalidArgumentException If the widget is invalid.
117
     * @return DashboardInterface Chainable
118
     */
119
    public function addWidget($widgetIdent, $widget)
120
    {
121
        if (!is_string($widgetIdent)) {
122
            throw new InvalidArgumentException(
123
                'Widget identifier needs to be a string'
124
            );
125
        }
126
127
        if ($widget instanceof UiItemInterface) {
128
            $this->widgets[$widgetIdent] = $widget;
129
        } elseif (is_array($widget)) {
130
            if (!isset($widget['ident'])) {
131
                $widget['ident'] = $widgetIdent;
132
            }
133
134
            $w = $this->widgetBuilder->build($widget);
135
136
            $this->widgets[$widgetIdent] = $w;
137
        } else {
138
            throw new InvalidArgumentException(
139
                'Can not add widget: Invalid Widget.'
140
            );
141
        }
142
143
        return $this;
144
    }
145
146
    /**
147
     * Retrieve the dashboard's widgets.
148
     *
149
     * @param callable $widgetCallback A callback applied to each widget.
150
     * @return UiItemInterface[]|Generator
151
     */
152
    public function widgets(callable $widgetCallback = null)
0 ignored issues
show
Coding Style introduced by
widgets 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...
153
    {
154
        $widgets = $this->widgets;
155
        uasort($widgets, [ $this, 'sortItemsByPriority' ]);
156
157
        $widgetCallback = isset($widgetCallback) ? $widgetCallback : $this->widgetCallback;
158
        foreach ($widgets as $widget) {
159
            if (isset($widget['permissions']) && $this instanceof AuthAwareInterface) {
160
                $widget->setActive($this->hasPermissions($widget['permissions']));
161
            }
162
163
            if (!$widget->active()) {
164
                continue;
165
            }
166
167
            if ($widgetCallback) {
168
                $widgetCallback($widget);
169
            }
170
171
            $GLOBALS['widget_template'] = $widget->template();
172
173
            yield $widget;
174
175
            $GLOBALS['widget_template'] = '';
176
        }
177
    }
178
179
    /**
180
     * Determine if the dashboard has any widgets.
181
     *
182
     * @return boolean
183
     */
184
    public function hasWidgets()
185
    {
186
        return ($this->numWidgets() > 0);
187
    }
188
189
    /**
190
     * Count the number of widgets attached to the dashboard.
191
     *
192
     * @return integer
193
     */
194
    public function numWidgets()
195
    {
196
        return count($this->widgets);
197
    }
198
}
199