在很多系统中会有层级体系,特别是在各种管理系统的菜单、分类、部门等相关模块。一般而言,系统都会把这些对象保存在数据库中,并用一个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 9030