前言
博客的文章分类菜单没有层级关系,一直是不太舒适的地方。于是抽空将菜单重新修改,整理为树状结构以体现层级关系。
关于菜单的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方法解析。
- to方法:
<?php
$this->widget("Widget_Metas_Category_List")->to($rows);
// 获取所有的菜单信息
while($rows->next()){
...
}
?>- 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;
?> 

Comments | NOTHING