2025-04-10 23:19:13 +08:00

223 lines
6.6 KiB
PHP
Executable File

<?php
namespace mdm\admin\components;
use Yii;
use yii\caching\TagDependency;
use mdm\admin\models\Menu;
/**
* MenuHelper used to generate menu depend of user role.
* Usage
*
* ```
* use mdm\admin\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
{
/**
* Use to get assigned menu of user.
* @param mixed $userId
* @param integer $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 boolean $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' => Configs::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' => Configs::CACHE_TAG
]));
}
}
return $result;
}
/**
* 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 integer $parent
* @return array
*/
private static function normalizeMenu(&$assigned, &$menus, $callback, $parent = null)
{
$result = [];
$order = [];
foreach ($assigned as $id) {
$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']),
];
if ($menu['children'] != []) {
$item['items'] = $menu['children'];
}
}
$result[] = $item;
$order[] = $menu['order'];
}
}
if ($result != []) {
array_multisort($order, $result);
}
return $result;
}
}