PHP学级与年级的转换函数,PHP常用函数 无限级菜单/权限树设计与实现

本文介绍了如何使用数据库设计和算法处理实现无限级菜单和权限树。通过在`SuperUserMenus`表中使用`pid`字段表示父子关系,实现了菜单的无限级递归。利用PHP的`array_column`函数处理数据,并通过自定义`gettreeitems`函数进行树形分类,将数据转换为便于展示的父子结构。文章还提供了相关代码示例,展示了如何在ThinkPHP5框架中动态加载和展示菜单。

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

导语

在开发中我们经常会遇到:导航菜单、部门菜单、权限树、评论等功能。

这些功能都有共同的特点:

有父子关系

可无限递归

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

数据库设计

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来进行区分父子关系,就是将二维数组进行树形划分来实现。

本文同步分享在 博客“Meng小羽”(other)。

如有侵权,请联系 support@oschina.cn 删除。

本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值