Issues (81)

components/MenuHelper.php (1 issue)

Labels
Severity
1
<?php
2
3
namespace toir427\admin\components;
4
5
use Yii;
6
use yii\caching\TagDependency;
7
use toir427\admin\models\Menu;
8
9
/**
10
 * MenuHelper used to generate menu depend of user role.
11
 * Usage
12
 * 
13
 * ```
14
 * use toir427\admin\components\MenuHelper;
15
 * use yii\bootstrap\Nav;
16
 *
17
 * echo Nav::widget([
18
 *    'items' => MenuHelper::getAssignedMenu(Yii::$app->user->id)
19
 * ]);
20
 * ```
21
 * 
22
 * To reformat returned, provide callback to method.
23
 * 
24
 * ```
25
 * $callback = function ($menu) {
26
 *    $data = eval($menu['data']);
27
 *    return [
28
 *        'label' => $menu['name'],
29
 *        'url' => [$menu['route']],
30
 *        'options' => $data,
31
 *        'items' => $menu['children']
32
 *        ]
33
 *    ]
34
 * }
35
 *
36
 * $items = MenuHelper::getAssignedMenu(Yii::$app->user->id, null, $callback);
37
 * ```
38
 *
39
 * @author Misbahul D Munir <[email protected]>
40
 * @since 1.0
41
 */
42
class MenuHelper
43
{
44
    /**
45
     * Use to get assigned menu of user.
46
     * @param mixed $userId
47
     * @param integer $root
48
     * @param \Closure $callback use to reformat output.
49
     * callback should have format like
50
     * 
51
     * ```
52
     * function ($menu) {
53
     *    return [
54
     *        'label' => $menu['name'],
55
     *        'url' => [$menu['route']],
56
     *        'options' => $data,
57
     *        'items' => $menu['children']
58
     *        ]
59
     *    ]
60
     * }
61
     * ```
62
     * @param boolean  $refresh
63
     * @return array
64
     */
65
    public static function getAssignedMenu($userId, $root = null, $callback = null, $refresh = false)
66
    {
67
        $config = Configs::instance();
68
69
        /* @var $manager \yii\rbac\BaseManager */
70
        $manager = Configs::authManager();
71
        $menus = Menu::find()->asArray()->indexBy('id')->all();
72
        $key = [__METHOD__, $userId, $manager->defaultRoles];
73
        $cache = $config->cache;
74
75
        if ($refresh || $cache === null || ($assigned = $cache->get($key)) === false) {
76
            $routes = $filter1 = $filter2 = [];
77
            if ($userId !== null) {
78
                foreach ($manager->getPermissionsByUser($userId) as $name => $value) {
79
                    if ($name[0] === '/') {
80
                        if (substr($name, -2) === '/*') {
81
                            $name = substr($name, 0, -1);
82
                        }
83
                        $routes[] = $name;
84
                    }
85
                }
86
            }
87
            foreach ($manager->defaultRoles as $role) {
88
                foreach ($manager->getPermissionsByRole($role) as $name => $value) {
89
                    if ($name[0] === '/') {
90
                        if (substr($name, -2) === '/*') {
91
                            $name = substr($name, 0, -1);
92
                        }
93
                        $routes[] = $name;
94
                    }
95
                }
96
            }
97
            $routes = array_unique($routes);
98
            sort($routes);
99
            $prefix = '\\';
100
            foreach ($routes as $route) {
101
                if (strpos($route, $prefix) !== 0) {
102
                    if (substr($route, -1) === '/') {
103
                        $prefix = $route;
104
                        $filter1[] = $route . '%';
105
                    } else {
106
                        $filter2[] = $route;
107
                    }
108
                }
109
            }
110
            $assigned = [];
111
            $query = Menu::find()->select(['id'])->asArray();
112
            if (count($filter2)) {
113
                $assigned = $query->where(['route' => $filter2])->column();
114
            }
115
            if (count($filter1)) {
116
                $query->where('route like :filter');
117
                foreach ($filter1 as $filter) {
118
                    $assigned = array_merge($assigned, $query->params([':filter' => $filter])->column());
119
                }
120
            }
121
            $assigned = static::requiredParent($assigned, $menus);
122
            if ($cache !== null) {
123
                $cache->set($key, $assigned, $config->cacheDuration, new TagDependency([
124
                    'tags' => Configs::CACHE_TAG
125
                ]));
126
            }
127
        }
128
129
        $key = [__METHOD__, $assigned, $root];
130
        if ($refresh || $callback !== null || $cache === null || (($result = $cache->get($key)) === false)) {
131
            $result = static::normalizeMenu($assigned, $menus, $callback, $root);
132
            if ($cache !== null && $callback === null) {
133
                $cache->set($key, $result, $config->cacheDuration, new TagDependency([
134
                    'tags' => Configs::CACHE_TAG
135
                ]));
136
            }
137
        }
138
139
        return $result;
140
    }
141
142
    /**
143
     * Ensure all item menu has parent.
144
     * @param  array $assigned
145
     * @param  array $menus
146
     * @return array
147
     */
148
    private static function requiredParent($assigned, &$menus)
149
    {
150
        $l = count($assigned);
151
        for ($i = 0; $i < $l; $i++) {
152
            $id = $assigned[$i];
153
            $parent_id = $menus[$id]['parent'];
154
            if ($parent_id !== null && !in_array($parent_id, $assigned)) {
155
                $assigned[$l++] = $parent_id;
156
            }
157
        }
158
159
        return $assigned;
160
    }
161
162
    /**
163
     * Parse route
164
     * @param  string $route
165
     * @return mixed
166
     */
167
    public static function parseRoute($route)
168
    {
169
        if (!empty($route)) {
170
            $url = [];
171
            $r = explode('&', $route);
172
            $url[0] = $r[0];
173
            unset($r[0]);
174
            foreach ($r as $part) {
175
                $part = explode('=', $part);
176
                $url[$part[0]] = isset($part[1]) ? $part[1] : '';
177
            }
178
179
            return $url;
180
        }
181
182
        return '#';
183
    }
184
185
    /**
186
     * Normalize menu
187
     * @param  array $assigned
188
     * @param  array $menus
189
     * @param  Closure $callback
0 ignored issues
show
The type toir427\admin\components\Closure was not found. Did you mean Closure? If so, make sure to prefix the type with \.
Loading history...
190
     * @param  integer $parent
191
     * @return array
192
     */
193
    private static function normalizeMenu(&$assigned, &$menus, $callback, $parent = null)
194
    {
195
        $result = [];
196
        $order = [];
197
        foreach ($assigned as $id) {
198
            $menu = $menus[$id];
199
            if ($menu['parent'] == $parent) {
200
                $menu['children'] = static::normalizeMenu($assigned, $menus, $callback, $id);
201
                if ($callback !== null) {
202
                    $item = call_user_func($callback, $menu);
203
                } else {
204
                    $item = [
205
                        'label' => $menu['name'],
206
                        'url' => static::parseRoute($menu['route']),
207
                    ];
208
                    if ($menu['children'] != []) {
209
                        $item['items'] = $menu['children'];
210
                    }
211
                }
212
                $result[] = $item;
213
                $order[] = $menu['order'];
214
            }
215
        }
216
        if ($result != []) {
217
            array_multisort($order, $result);
218
        }
219
220
        return $result;
221
    }
222
}
223