基于php的前端页面菜单树的实现 最后更新时间:2024年03月23日 ### 前言 博客的文章分类菜单没有层级关系,一直是不太舒适的地方。于是抽空将菜单重新修改,整理为树状结构以体现层级关系。 ### 关于菜单的row数据 菜单的数据来源是Typecho的分类创建的,可以通过调用`$this->widget("Widget_Metas_Category_List")`来获取到相关的数据。 通过该方法获取到的row的结构大致如下: ``` Row{ **字段** mid:分类id name:分类名称 slug:分类缩写名 type:分类类型,譬如categorery description:分类的描述 count:该分类下的文章数目 order: parent:父分类的mid levels:所在的层级 directory:Array类型,数组元素是每层分类的slug permalink:该分类的url **可用参数** ignore 不显示的分类mid current 当前分类mid,如果设置了,则会在输出是增加class="category-active"样式 } ``` 有两种获取办法,一种是通过to方法获取迭代对象后,通过迭代来获得,一种是直接通过parse方法解析。 1. to方法: ```php widget("Widget_Metas_Category_List")->to($rows); // 获取所有的菜单信息 while($rows->next()){ ... } ?> ``` 2. parse方法 ```php widget('Widget_Metas_Category_List')->parse('{name} ({count})'); ?> ``` ### 实现思路 从row的数据信息可以看出,row是自带了父元素的id和自身的层级的。 因此,可以通过先取出第一层级的目录,再取出第二层级,第三层级等等目录的方式,来实现层级的划分,最后按照层级逐步找到目录相对应的父层级并包裹即可。 难点在于,row所返回的数据是行数据且是混乱无序的。所以需要先对row进行解析,构建成一棵有序的树,才能方便于接下来的处理。 创建解析器,构建树 ```php now_array = $now_array; $this->menu = new Row(); $this->menu->mid = 0; $this->menu->child = array(); } // 构建索引 function buidIndex($row_array,$set_array){ foreach($row_array as $key=>$row){ $set_array[$row->mid] = $row; } } // 搜索row function searchRow($row,$mid){ // 从menu的child中搜索 if($row->mid == $mid){ return $row; }elseif(count($row->child)>0){ foreach($row->child as $key=>$child){ $row = $this->searchRow($child,$mid); if($row != null){ return $row; } } }else{ return null; } return null; } // 构建 row树 function buidTree(){ $num = 0; while($this->stopLoop()){ $parent_row = $this->searchRow($this->menu,$this->now_array[$num]->parent); if($parent_row != null){ array_push($parent_row->child,$this->now_array[$num]); }else{ array_push($this->unallocated_array,$this->now_array[$num]); } $num += 1; if($num == count($this->now_array)){ echo ""; $num=0; $this->now_array = $this->unallocated_array; $this->unallocated_array = array(); } } return $this->menu; } // 停止循环 function stopLoop(){ if(count($this->now_array) == 0 && count($this->unallocated_array) == 0){ return false; } return true; } } ?> ``` 完成树的构建以后,整个流程就简单多了。因为这棵树已经能够很好地体现目录的层级了。 接下来就只需要将这棵树构建成我们所需的html代码。 思路相对简单。从树的顶节点开始,当存在父节点存在子节点的时,就往下递归,一直到子节点的根部位置(即没有了新的子节点)为止。将子节点的信息处理为相应的dom代码,然后一层一层返回,再由父节点一层一层包裹。直到最后从顶节点返回出去。 构建dom: ```php // 构建html class BuidHtml{ var $menu; var $html; function __construct($menu){ $this->menu = $menu; } // 构建html function buidHtml($row,$leval){ $child_html=""; if($row->child != null){ foreach($row->child as $key=>$child){ $child_html .= $this->buidHtml($child,$leval+1); } } if($child_html!= ""&& $row->mid != 0){ $child_html = "{$child_html}"; } if($row->mid == 0){ return $child_html; } return "{$row->name}{$child_html}"; } } ``` 自此,通过这两个解析器和构造器,就足够将后端获取到的数据转换为有着层级的html代码了。 ### 完整代码 ```php now_array = $now_array; $this->menu = new Row(); $this->menu->mid = 0; $this->menu->child = array(); } // 构建索引 function buidIndex($row_array,$set_array){ foreach($row_array as $key=>$row){ $set_array[$row->mid] = $row; } } // 搜索row function searchRow($row,$mid){ // 从menu的child中搜索 if($row->mid == $mid){ return $row; }elseif(count($row->child)>0){ foreach($row->child as $key=>$child){ $row = $this->searchRow($child,$mid); if($row != null){ return $row; } } }else{ return null; } return null; } // 构建 row树 function buidTree(){ $num = 0; while($this->stopLoop()){ $parent_row = $this->searchRow($this->menu,$this->now_array[$num]->parent); if($parent_row != null){ array_push($parent_row->child,$this->now_array[$num]); }else{ array_push($this->unallocated_array,$this->now_array[$num]); } $num += 1; if($num == count($this->now_array)){ echo ""; $num=0; $this->now_array = $this->unallocated_array; $this->unallocated_array = array(); } } return $this->menu; } // 停止循环 function stopLoop(){ if(count($this->now_array) == 0 && count($this->unallocated_array) == 0){ return false; } return true; } } // 构建html class BuidHtml{ var $menu; var $html; function __construct($menu){ $this->menu = $menu; } // 构建html function buidHtml($row,$leval){ $child_html=""; if($row->child != null){ foreach($row->child as $key=>$child){ $child_html .= $this->buidHtml($child,$leval+1); } } if($child_html!= ""&& $row->mid != 0){ $child_html = "{$child_html}"; } if($row->mid == 0){ return $child_html; } return "{$row->name}{$child_html}"; } } $this->widget("Widget_Metas_Category_List")->to($rows); $row_array = array(); $num = 0; // 获取所有的菜单信息 while($rows->next()){ $row = new Row(); $row->name = $rows->name; $row->permalink = $rows->permalink; $row->mid = $rows->mid; $row->parent = $rows->parent; $row->child = array(); $row_array[$num]=$row; $num += 1; } $paramRow = new ParamRow($row_array); $menu = $paramRow->buidTree(); $buidHtml = new BuidHtml($menu); $html = $buidHtml->buidHtml($buidHtml->menu,1); echo $html; ?> ```
Comments | NOTHING