123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- <?php
- namespace common\modules\rbac\components;
- use common\modules\rbac\models\Menu;
- use Yii;
- use yii\caching\TagDependency;
- /**
- * MenuHelper used to generate menu depend of user role.
- * Usage.
- *
- * ~~~
- * use common\modules\rbac\components\MenuHelper;
- * use yii\bootstrap\Nav;
- *
- * echo Nav::widget([
- * 'items' => MenuHelper::getAssignedMenu(Yii::$app->user->id)
- * ]);
- * ~~~
- *
- * To reformat returned, provide callback to method.
- *
- * ~~~
- * $callback = function ($menu) {
- * $data = eval($menu['data']);
- * return [
- * 'label' => $menu['name'],
- * 'url' => [$menu['route']],
- * 'options' => $data,
- * 'items' => $menu['children']
- * ]
- * ]
- * }
- *
- * $items = MenuHelper::getAssignedMenu(Yii::$app->user->id, null, $callback);
- * ~~~
- *
- * @author Misbahul D Munir <misbahuldmunir@gmail.com>
- *
- * @since 1.0
- */
- class MenuHelper
- {
- const CACHE_TAG = 'mdm.admin.menu';
- /**
- * Use to get assigned menu of user.
- *
- * @param mixed $userId
- * @param int $root
- * @param \Closure $callback use to reformat output.
- * callback should have format like
- *
- * ~~~
- * function ($menu) {
- * return [
- * 'label' => $menu['name'],
- * 'url' => [$menu['route']],
- * 'options' => $data,
- * 'items' => $menu['children']
- * ]
- * ]
- * }
- * ~~~
- * @param bool $refresh
- *
- * @return array
- */
- public static function getAssignedMenu($userId, $root = null, $callback = null, $refresh = false)
- {
- $config = Configs::instance();
- /* @var $manager \yii\rbac\BaseManager */
- $manager = Yii::$app->getAuthManager();
- $menus = Menu::find()->asArray()->indexBy('id')->all();
- $key = [__METHOD__, $userId, $manager->defaultRoles];
- $cache = $config->cache;
- if ($refresh || $cache === null || ($assigned = $cache->get($key)) === false) {
- $routes = $filter1 = $filter2 = [];
- if ($userId !== null) {
- foreach ($manager->getPermissionsByUser($userId) as $name => $value) {
- if ($name[0] === '/') {
- if (substr($name, -2) === '/*') {
- $name = substr($name, 0, -1);
- }
- $routes[] = $name;
- }
- }
- }
- foreach ($manager->defaultRoles as $role) {
- foreach ($manager->getPermissionsByRole($role) as $name => $value) {
- if ($name[0] === '/') {
- if (substr($name, -2) === '/*') {
- $name = substr($name, 0, -1);
- }
- $routes[] = $name;
- }
- }
- }
- $routes = array_unique($routes);
- sort($routes);
- $prefix = '\\';
- foreach ($routes as $route) {
- if (strpos($route, $prefix) !== 0) {
- if (substr($route, -1) === '/') {
- $prefix = $route;
- $filter1[] = $route.'%';
- } else {
- $filter2[] = $route;
- }
- }
- }
- $assigned = [];
- $query = Menu::find()->select(['id'])->asArray();
- if (count($filter2)) {
- $assigned = $query->where(['route' => $filter2])->column();
- }
- if (count($filter1)) {
- $query->where('route like :filter');
- foreach ($filter1 as $filter) {
- $assigned = array_merge($assigned, $query->params([':filter' => $filter])->column());
- }
- }
- $assigned = static::requiredParent($assigned, $menus);
- if ($cache !== null) {
- $cache->set($key, $assigned, $config->cacheDuration, new TagDependency([
- 'tags' => self::CACHE_TAG,
- ]));
- }
- }
- $key = [__METHOD__, $assigned, $root];
- if ($refresh || $callback !== null || $cache === null || (($result = $cache->get($key)) === false)) {
- $result = static::normalizeMenu($assigned, $menus, $callback, $root);
- if ($cache !== null && $callback === null) {
- $cache->set($key, $result, $config->cacheDuration, new TagDependency([
- 'tags' => self::CACHE_TAG,
- ]));
- }
- }
- return $result;
- }
- /**
- * 获取navs中第一个有url的nav.
- *
- * @param unknown $navs
- */
- public static function getFirstMenu($navs)
- {
- foreach ($navs as $nav) {
- if (array_key_exists('url', $nav) && $nav['url'] != '#') {
- return $nav;
- }
- if (array_key_exists('items', $nav) && count($nav['items']) > 0) {
- $nav = static::getFirstMenu($nav['items']);
- if ($nav != null) {
- return $nav;
- }
- }
- }
- return;
- }
- public static function getSiblingsMenu($label, $navs)
- {
- foreach ($navs as $nav) {
- if($nav['label'] == $label) {
- return $navs;
- }
- if (array_key_exists('items', $nav) && count($nav['items']) > 0) {
- $n = self::getSiblingsMenu($label, $nav['items']);
- if (!empty($n)) {
- return $n;
- }
- }
- }
- return [];
- }
- public static function getRootMenu($nav)
- {
- if ($nav['parent'] == null) {
- return $nav;
- }
- $parent = Menu::find()->where(['id' => $nav['parent']])->one();
- if ($parent['parent'] != null) {
- return static::getRootMenu($parent);
- } else {
- return $parent;
- }
- }
- /**
- * Ensure all item menu has parent.
- *
- * @param array $assigned
- * @param array $menus
- *
- * @return array
- */
- private static function requiredParent($assigned, &$menus)
- {
- $l = count($assigned);
- for ($i = 0; $i < $l; ++$i) {
- $id = $assigned[$i];
- $parent_id = $menus[$id]['parent'];
- if ($parent_id !== null && !in_array($parent_id, $assigned)) {
- $assigned[$l++] = $parent_id;
- }
- }
- return $assigned;
- }
- /**
- * Parse route.
- *
- * @param string $route
- *
- * @return mixed
- */
- public static function parseRoute($route)
- {
- if (!empty($route)) {
- $url = [];
- $r = explode('&', $route);
- $url[0] = $r[0];
- unset($r[0]);
- foreach ($r as $part) {
- $part = explode('=', $part);
- $url[$part[0]] = isset($part[1]) ? $part[1] : '';
- }
- return $url;
- }
- return '#';
- }
- /**
- * Normalize menu.
- *
- * @param array $assigned
- * @param array $menus
- * @param Closure $callback
- * @param int $parent
- *
- * @return array
- */
- private static function normalizeMenu(&$assigned, &$menus, $callback, $parent = null)
- {
- $result = [];
- $order = [];
- foreach ($assigned as $id) {
- if(!isset($menus[$id])) {
- continue;
- }
- $menu = $menus[$id];
- if ($menu['parent'] == $parent) {
- $menu['children'] = static::normalizeMenu($assigned, $menus, $callback, $id);
- if ($callback !== null) {
- $item = call_user_func($callback, $menu);
- } else {
- $item = [
- 'label' => $menu['name'],
- 'url' => static::parseRoute($menu['route']),
- 'icon' => $menu['icon']
- ];
- if ($menu['children'] != []) {
- $item['items'] = $menu['children'];
- }
- }
- $result[] = $item;
- $order[] = $menu['order'];
- }
- }
- if ($result != []) {
- array_multisort($order, $result);
- }
- return $result;
- }
- /**
- * Use to invalidate cache.
- */
- public static function invalidate()
- {
- if (Configs::instance()->cache !== null) {
- TagDependency::invalidate(Configs::instance()->cache, self::CACHE_TAG);
- }
- }
- }
|