基于php的前端页面菜单树的实现


前言

博客的文章分类菜单没有层级关系,一直是不太舒适的地方。于是抽空将菜单重新修改,整理为树状结构以体现层级关系。

关于菜单的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
$this->widget("Widget_Metas_Category_List")->to($rows);
// 获取所有的菜单信息
while($rows->next()){
...
}
?>
  1. parse方法
<?php
$this->widget('Widget_Metas_Category_List')->parse('<li><a href="{permalink}">{name}</a> ({count})</li>'); ?>
</ul>

实现思路

从row的数据信息可以看出,row是自带了父元素的id和自身的层级的。
因此,可以通过先取出第一层级的目录,再取出第二层级,第三层级等等目录的方式,来实现层级的划分,最后按照层级逐步找到目录相对应的父层级并包裹即可。
难点在于,row所返回的数据是行数据且是混乱无序的。所以需要先对row进行解析,构建成一棵有序的树,才能方便于接下来的处理。
创建解析器,构建树

<?php
// 解析row
class ParamRow{
    var $unallocated_array = array();
    var $now_array = array();
    var $now_mid = 0;
    var $menu;
    function __construct($now_array){
        $this->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:

// 构建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 = "<div class='sub-menu' >{$child_html}</div>";
        }
        if($row->mid == 0){
            return $child_html;
        }
        return "<li id = '{$row->mid}'><a href='{$row->permalink}'>{$row->name}</a></li>{$child_html}";
    }
}

自此,通过这两个解析器和构造器,就足够将后端获取到的数据转换为有着层级的html代码了。

完整代码

<?php
class Row{
    var $name;
    var $permalink;
    var $mid;
    var $parent;
    var $child;
}
// 解析row
class ParamRow{
    var $unallocated_array = array();
    var $now_array = array();
    var $now_mid = 0;
    var $menu;
    function __construct($now_array){
        $this->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 = "<div class='sub-menu' >{$child_html}</div>";
        }
        if($row->mid == 0){
            return $child_html;
        }
        return "<li id = '{$row->mid}'><a href='{$row->permalink}'>{$row->name}</a></li>{$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;
?>

声明:一代明君的小屋|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 基于php的前端页面菜单树的实现


欢迎来到我的小屋