专栏首页Debug客栈PHP常用函数 无限级菜单/权限树设计与实现

PHP常用函数 无限级菜单/权限树设计与实现

导语

在开发中我们经常会遇到:导航菜单、部门菜单、权限树、评论等功能。 这些功能都有共同的特点:

  1. 有父子关系
  2. 可无限递归

以导航菜单为例, 将导航菜单设置为动态的, 即从动态加载菜单数据。

数据库设计

CREATE TABLE `SuperUserMenus` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `pid` int(11) NOT NULL COMMENT '父级ID',
  `order` int(11) NOT NULL DEFAULT '0' COMMENT '菜单排序',
  `title` varchar(100) NOT NULL COMMENT '菜单标题',
  `controller` varchar(100) DEFAULT NULL COMMENT '控制器名称',
  `method` varchar(100) DEFAULT NULL COMMENT '方法名称',
  `ishidden` int(1) NOT NULL DEFAULT '0' COMMENT '是否隐藏:0正常显示,1隐藏',
  `status` int(1) NOT NULL DEFAULT '0' COMMENT '状态:0正常,1禁用',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8;

在这里用作分级的表示字段就是pid,用作查找对应父ID,一个菜单一方面自己可以具有父ID,可以有一个父级菜单,另一方面可以用作父级,子级来定义该父级ID,这样就可以设计无限级菜单,这样设计好处是可以父子级别菜单同表存储,便于遍历显示,但是存储在表中的数据只有对应逻辑,不好在数据库中维护及查看,需要写一下算法进行可视化遍历。

数据封装

使用算法进行封装读取之后,使得父子关系一目了然,包含关系,如下显示:

array(8) {
  [0] => array(9) {
    ["id"] => int(1)
    ["pid"] => int(0)
    ["order"] => int(0)
    ["title"] => string(18) "超级用户管理"
    ["controller"] => string(0) ""
    ["method"] => string(0) ""
    ["ishidden"] => int(0)
    ["status"] => int(0)
    ["children"] => array(1) {
      [0] => array(8) {
        ["id"] => int(3)
        ["pid"] => int(1)
        ["order"] => int(0)
        ["title"] => string(18) "超级用户列表"
        ["controller"] => string(14) "admin"
        ["method"] => string(5) "index"
        ["ishidden"] => int(0)
        ["status"] => int(0)
      }
    }
  }
}

算法转换

在这里使用ThinkPHP5这个框架来进行编写,虽然语言及框架不同,但是思路及使用算法函数都是一样的,首先将对应用户下菜单json存储数组读取出并进行索引处理:

/**
 * 动态菜单显示操作
 * @return string
 * @throws DataNotFoundException
 * @throws ModelNotFoundException
 * @throws DbException
 */
public function index()
{
    $Super_id = 1;
    $menus = false;
    $role = Db::table('SuperUser')->where('Super_id', $Super_id)->select();
    $role = $role[0];
    if ($role) {
        $role['rights'] = (isset($role['rights']) && $role['rights']) ? json_decode($role['rights'], true) : [];
    }
    if ($role['rights']) {
        $menus = Db::query('select * from SuperUserMenus where id in(' . implode(',', $role['rights']) . ') and ishidden=0 and status=0');

        $menus = $this->_array_column($menus, null, 'id');
        $menus && $menus = $this->gettreeitems($menus);
    }

    return json_encode($menus);
}

之后将ID作为二维数组中的唯一索引,这里使用array_column函数,由于这个函数只支持PHP5.5+版本,低版本不支持,我将此函数放在此处:

/**
 * PHP5.5+ array_column函数
 * @param null $input
 * @param null $columnKey
 * @param null $indexKey
 * @return array|bool|null
 */
public function _array_column($input = null, $columnKey = null, $indexKey = null)
{
    // Using func_get_args() in order to check for proper number of
    // parameters and trigger errors exactly as the built-in array_column()
    // does in PHP 5.5.
    $argc = func_num_args();
    $params = func_get_args();
    if ($argc < 2) {
        trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING);
        return null;
    }
    if (!is_array($params[0])) {
        trigger_error(
            'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
            E_USER_WARNING
        );
        return null;
    }
    if (!is_int($params[1])
        && !is_float($params[1])
        && !is_string($params[1])
        && $params[1] !== null
        && !(is_object($params[1]) && method_exists($params[1], '__toString'))
    ) {
        trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
        return false;
    }
    if (isset($params[2])
        && !is_int($params[2])
        && !is_float($params[2])
        && !is_string($params[2])
        && !(is_object($params[2]) && method_exists($params[2], '__toString'))
    ) {
        trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
        return false;
    }
    $paramsInput = $params[0];
    $paramsColumnKey = ($params[1] !== null) ? (string)$params[1] : null;
    $paramsIndexKey = null;
    if (isset($params[2])) {
        if (is_float($params[2]) || is_int($params[2])) {
            $paramsIndexKey = (int)$params[2];
        } else {
            $paramsIndexKey = (string)$params[2];
        }
    }
    $resultArray = array();
    foreach ($paramsInput as $row) {
        $key = $value = null;
        $keySet = $valueSet = false;
        if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
            $keySet = true;
            $key = (string)$row[$paramsIndexKey];
        }
        if ($paramsColumnKey === null) {
            $valueSet = true;
            $value = $row;
        } elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) {
            $valueSet = true;
            $value = $row[$paramsColumnKey];
        }
        if ($valueSet) {
            if ($keySet) {
                $resultArray[$key] = $value;
            } else {
                $resultArray[] = $value;
            }
        }
    }
    return $resultArray;
}

最后将数组进行树形分类,将同属于一个父级ID的子元素归类至children下:

/**
 * 子节点分级显示
 * @param $items
 * @return array
 */
private function gettreeitems($items)
    {
        $tree = array();
        foreach ($items as $item) {
            if (isset($items[$item['pid']])) {
                $items[$item['pid']]['children'][] = &$items[$item['id']];
            } else {
                $tree[] = &$items[$item['id']];
            }
        }
        return $tree;
    }

结语

无限级菜单/权限树设计原理就是使用pid来进行区分父子关系,就是将二维数组进行树形划分来实现。

本文链接:https://www.debuginn.cn/4549.html

本文采用CC BY-NC-SA 3.0 Unported协议进行许可,转载请保留此文章链接

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • PHP常用函数 字符串处理函数

    友情提示:此篇文章大约需要阅读 13分钟58秒,不足之处请多指教,感谢你的阅读。订阅本站

    Debug客栈
  • PHP常用函数 原生导出Excel文件

    近期在项目中做到了导出Excel功能,但是由于项目PHP版本为5.4,网上找了一大堆的插件或扩展由于封装发布版本较新,不支持PHP5.4版本,大多支持PHP7....

    Debug客栈
  • PHP常用函数 常用数组函数

    数组是PHP中一个常见的数据类型,其中PHP封装了许多有关数组处理的PHP函数,过去的几个月由于使用框架进行逻辑操作多一些,对数据的操作特别是数组的操作少之又少...

    Debug客栈
  • 如何用机器学习预测超售,避免美联航“暴力赶客”悲剧

    大数据文摘
  • iOS开发中使用算法之冒泡法

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

    用户1451823
  • 读书笔记《PHP与MySQL程序设计》一

    第1章 PHP概述 1.1  历史(PHP4、PHP5、PHP5.3、PHP6[未发布]) 1.2 一般语言特性(实用性、强大功能、可选择性、成本[开源]) 第...

    小古哥
  • linux awk 数组和循环

    awk 中数组叫做关联数组(associative arrays),下标可以是数字也可以是字符串。awk 中的数组不必提前声明,也不必声明大小,初始化数组元素用...

    阳光岛主
  • Numpy中的转置轴对换

    转置是重塑的一种特殊形式。转置返回源数组的视图,源数组和对源数组进行转置操作后返回的数组指向的是同一个地址。Numpy中有三种方式能够对数组进行转置操作:

    触摸壹缕阳光
  • 原 快速创建 HTML5 Canvas 电

    HT_hightopo
  • PHP常用库函数介绍+常见疑难问题解答

          最近在苦学PHP,虽然PHP在整体功能上不如Java强大,但相比PHP而言Java算是较重量级的,所以在小中型系统的开发上,使用PHP的趋势不可挡,...

    猿人谷

扫码关注云+社区

领取腾讯云代金券