在很多系统中会有层级体系,特别是在各种管理系统的菜单、分类、部门等相关模块。一般而言,系统都会把这些对象保存在数据库中,并用一个parent_id字段去记录该对象的父级对象。那么问题来了,当从数据库中重新查出对象列表后,如何重新恢复其层级关系呢?下面的这个函数可以帮到你:
<?php
/**
* 构建层级(树状)数组
* @param array $array 要进行处理的一维数组,经过该函数处理后,该数组自动转为树状数组
* @param string $pid 父级ID的字段名
* @return array|bool
*/
function array_tree(&$array, $pid = 'pid') {
// 子元素计数器
function array_children_count($array,$pid) {
$counter = array();
foreach($array as $item) {
$count = isset($counter[$item[$pid]]) ? $counter[$item[$pid]] : 0;
$count ++;
$counter[$item[$pid]] = $count;
}
return $counter;
}
// 把元素插入到对应的父元素children字段
function array_child_append($parent,$pid,$child) {
foreach($parent as &$item) {
if($item['id'] == $pid) {
if(!isset($item['children'])) $item['children'] = array();
$item['children'][] = $child;
}
}
return $parent;
}
// 开始程序
$counter = array_children_count($array,$pid);
// 如果顶级元素为0个,那么直接返回false
if($counter[0] == 0)
return false;
// 准备顶级元素
$tree = array();
// 位移
while(isset($counter[0]) && $counter[0] > 0) { // 如果顶级栏目的子元素计数器仍然大于0,那么仍然往下执行循环
$temp = array_shift($array);
if(isset($counter[$temp['id']]) && $counter[$temp['id']] > 0) { // 如果数组的第一个元素的子元素个数大于0,那么把该元素放置到数组的末端
array_push($array,$temp);
}
else { // 相反,如果该数组的第一个元素没有子元素,那么把该元素移动到其父元素的children字段中,同时,该元素从原数组中被删除
if($temp[$pid] == 0)
$tree[] = $temp;
else
$array = array_child_append($array,$temp[$pid],$temp);
}
$counter = array_children_count($array,$pid);
}
$array = $tree;
return $tree;
}
我来看下具体用法。
它有两个参数,第一个是传入的要处理的数组,注意,该数组必须是一维数组,而且所含字段有规定:①必须是一维数组,②必须包含id字段,③必须包含$pid对应的字段,④必须存在$pid为0的元素,也就是必须存在顶级元素。处理结束时,那些没有追溯到$pid=0的元素将被抛弃,只留下从$pid=0的顶级元素开始的树状结构,举个例子如下:
$arr = array(
array('id' => 53,'pid' => 0),
array('id' => 64,'pid' => 53),
array('id' => 70,'pid' => 42)
);
array_tree($arr,'pid');
由于上面的例子中id=70这个元素的pid=42,而id=42的元素并不存在,因此在最终结果中id=70的这个元素会被抛弃。
在使用过程中,array_tree处理后,树状数组会存在一个排序问题。处理结果并不会按照一定的规则进行升序或降序进行排列,而是以逻辑处理过程中的内存顺序排列,虽然树状层级是正确的,但是同级元素的顺序是不确定的。建议你通过array_orderby函数,对处理后的结果进行排序处理。
例子1:按从大到小规则排序的菜单
$demo1 = array(
1 => array('id' => 1,'title' => 'title1','pid' => 0),
2 => array('id' => 2,'title' => 'title2','pid' => 1),
3 => array('id' => 3,'title' => 'title3','pid' => 1),
4 => array('id' => 4,'title' => 'title4','pid' => 2),
5 => array('id' => 5,'title' => 'title5','pid' => 2),
6 => array('id' => 6,'title' => 'title6','pid' => 0)
);
$demo1 = array_tree($demo1);// 或者直接使用array_tree($demo1);,无需返回值
array_orderby($demo1,'id','asc','children');
print_r($demo1);
例子2:菜单排列不规律,父菜单在后面
$demo2 = array(
1 => array('id' => 1,'title' => 'title1','parent_id' => 2),
2 => array('id' => 2,'title' => 'title2','parent_id' => 0),
3 => array('id' => 3,'title' => 'title3','parent_id' => 1),
4 => array('id' => 4,'title' => 'title4','parent_id' => 2),
5 => array('id' => 5,'title' => 'title5','parent_id' => 6),
6 => array('id' => 6,'title' => 'title6','parent_id' => 0)
);
array_tree($demo2,'parent_id');
array_orderby($demo2,'id','asc','children');
print_r($demo2);
在上面的例子中可以看出,无论一维数组中实际的层级关系怎样复杂,该程序都能很好的进行处理。注意,array_orderby不是php的内置函数,你需要到上面链接文章中获取。
2016-02-03 10752


