PHP无限分类(树形类)的深入分析


Posted in PHP onJune 02, 2013

PHP无限分类,Google一下就能找到很多相关资料,思路比较拉风的,也是用得比较多的就是分类表至少有id,pid,name三个字段,id自增表分类,pid为父分类,name为分类名,这样就构成了一棵树,如下,算是我查询分类表得到的结果集。

<?php
//模拟PHP无限分类查询结果
return array(
    array(
        'id'=>1,
        'pid'=>0,
        'name'=>'主页'
    ),
    array(
        'id'=>2,
        'pid'=>0,
        'name'=>'新闻'
    ),
    array(
        'id'=>3,
        'pid'=>0,
        'name'=>'媒体'
    ),
    array(
        'id'=>4,
        'pid'=>0,
        'name'=>'下载'
    ),
    array(
        'id'=>5,
        'pid'=>0,
        'name'=>'关于我们'
    ),
    array(
        'id'=>6,
        'pid'=>2,
        'name'=>'天朝新闻'
    ),
    array(
        'id'=>7,
        'pid'=>2,
        'name'=>'海外新闻'
    ),
    array(
        'id'=>8,
        'pid'=>6,
        'name'=>'州官新闻'
    ),
    array(
        'id'=>9,
        'pid'=>3,
        'name'=>'音乐'
    ),
    array(
        'id'=>10,
        'pid'=>3,
        'name'=>'电影'
    ),
    array(
        'id'=>11,
        'pid'=>3,
        'name'=>'小说'
    ),
    array(
        'id'=>12,
        'pid'=>9,
        'name'=>'铃声'
    ),
    array(
        'id'=>13,
        'pid'=>9,
        'name'=>'流行音乐'
    ),
    array(
        'id'=>14,
        'pid'=>9,
        'name'=>'古典音乐'
    ),
    array(
        'id'=>15,
        'pid'=>12,
        'name'=>'热门铃声'
    ),
    array(
        'id'=>16,
        'pid'=>12,
        'name'=>'搞笑铃声'
    ),
    array(
        'id'=>17,
        'pid'=>12,
        'name'=>'MP3铃声'
    ),
    array(
        'id'=>18,
        'pid'=>17,
        'name'=>'128K'
    ),
    array(
        'id'=>19,
        'pid'=>8,
        'name'=>'娱乐新闻'
    ),
    array(
        'id'=>20,
        'pid'=>11,
        'name'=>'穿越类'
    ),
    array(
        'id'=>21,
        'pid'=>11,
        'name'=>'武侠类'
    ),
);
?>

拉风归拉风,但是那些文章提供的无限分类的类相关操作有点挫,直接把对数据库操作也封装进去了。也就是别人要用你这个类,还要跟你建一样的表,真TM恶心。由于项目要用到,所以自己写了一个PHP无限分类的类(也称树形类),没有数据库的操作,只需要实例化的时候传进去结果集,也就是树形数组。再执行leaf方法或navi方法即可得到想要的结果,下面请看源码,看完之后奉上smarty模板引擎的相应的模板递归方法。
<?php
/**
 * Tree 树型类(无限分类)
 *
 * @author Kvoid
 * @copyright http://kvoid.com
 * @version 1.0
 * @access public
 * @example
 *   $tree= new Tree($result);
 *   $arr=$tree->leaf(0);
 *   $nav=$tree->navi(15);
 */
class Tree {
    private $result;
    private $tmp;
    private $arr;
    private $already = array();
    /**
     * 构造函数
     *
     * @param array $result 树型数据表结果集
     * @param array $fields 树型数据表字段,array(分类id,父id)
     * @param integer $root 顶级分类的父id
     */
    public function __construct($result, $fields = array('id', 'pid'), $root = 0) {
        $this->result = $result;
        $this->fields = $fields;
        $this->root = $root;
        $this->handler();
    }
    /**
     * 树型数据表结果集处理
     */
    private function handler() {
        foreach ($this->result as $node) {
            $tmp[$node[$this->fields[1]]][] = $node;
        }
        krsort($tmp);
        for ($i = count($tmp); $i > 0; $i--) {
            foreach ($tmp as $k => $v) {
                if (!in_array($k, $this->already)) {
                    if (!$this->tmp) {
                        $this->tmp = array($k, $v);
                        $this->already[] = $k;
                        continue;
                    } else {
                        foreach ($v as $key => $value) {
                            if ($value[$this->fields[0]] == $this->tmp[0]) {
                                $tmp[$k][$key]['child'] = $this->tmp[1];
                                $this->tmp = array($k, $tmp[$k]);
                            }
                        }
                    }
                }
            }
            $this->tmp = null;
        }
        $this->tmp = $tmp;
    }
    /**
     * 反向递归
     */
    private function recur_n($arr, $id) {
        foreach ($arr as $v) {
            if ($v[$this->fields[0]] == $id) {
                $this->arr[] = $v;
                if ($v[$this->fields[1]] != $this->root) $this->recur_n($arr, $v[$this->fields[1]]);
            }
        }
    }
    /**
     * 正向递归
     */
    private function recur_p($arr) {
        foreach ($arr as $v) {
            $this->arr[] = $v[$this->fields[0]];
            if ($v['child']) $this->recur_p($v['child']);
        }
    }
    /**
     * 菜单 多维数组
     *
     * @param integer $id 分类id
     * @return array 返回分支,默认返回整个树
     */
    public function leaf($id = null) {
        $id = ($id == null) ? $this->root : $id;
        return $this->tmp[$id];
    }
    /**
     * 导航 一维数组
     *
     * @param integer $id 分类id
     * @return array 返回单线分类直到顶级分类
     */
    public function navi($id) {
        $this->arr = null;
        $this->recur_n($this->result, $id);
        krsort($this->arr);
        return $this->arr;
    }
    /**
     * 散落 一维数组
     *
     * @param integer $id 分类id
     * @return array 返回leaf下所有分类id
     */
    public function leafid($id) {
        $this->arr = null;
        $this->arr[] = $id;
        $this->recur_p($this->leaf($id));
        return $this->arr;
    }
}
?>

在smarty中的PHP无限分类的使用方法:
$result=$db->query(……);//这里查询得到结果集,注意结果集为数组
$tree= new Tree($result);
$arr=$tree->leaf(0);
$nav=$tree->navi(15);
$smarty->assign(‘arr',$arr);
$smarty->assign(‘nav',$nav);
$smarty->display(‘test.html');
在smarty模板中这样递归:
<!--导航-->
<div id="navigator">
<{foreach $nav as $n}>
    <{if $n@iteration != $n@last}>
        <{$n.name}> ->
    <{else}>
        <{$n.name}>
    <{/if}>
<{/foreach}>
</div>
<!--树形菜单-->
<div id="menu">
<{function name=menu}>
    <ul>
        <{foreach $data as $entry}>
            <li>
                <span><{$entry.name}></span> <{*注意字段要改成自己的字段哦*}>
            <{if isset($entry.child)}>
                <{call name=menu data=$entry.child}>
            <{/if}>
            </li>
        <{/foreach}>
    </ul>
<{/function}>
<{call name=menu data=$arr}> <{*注意在这里$arr才是模板变量*}>
</div>

当然,你也可以更改递归方法,用你想的标签不受拘束。HTML+PHP混编的递归方法这里就不贴了,我也懒得写,最讨厌混编,看着恶心,在这里推荐一下jake前辈的SpeedPHP框架,由于默认的引擎是smarty,我的这个PHP无限分类完全兼容SP框架。同样的,jquery的treeview插件和下拉菜单插件也完美支持。
对了,建议使用Smarty强大的缓存功能,缓存才是王道。
PHP 相关文章推荐
php正则
Jul 07 PHP
php session 检测和注销
Mar 16 PHP
PHP中strtotime函数使用方法详解
Nov 27 PHP
php获取服务器信息的实现代码
Feb 04 PHP
PHP执行批量mysql语句的解决方法
May 02 PHP
解析thinkphp的左右值无限分类
Jun 20 PHP
ThinkPHP CURD方法之order方法详解
Jun 18 PHP
php利用imagemagick实现复古老照片效果实例
Feb 16 PHP
thinkPHP5.0框架安装教程
Mar 25 PHP
PHP生成各种随机验证码的方法总结【附demo源码】
Jun 05 PHP
[原创]php token使用与验证示例【测试可用】
Aug 30 PHP
PHP生成指定范围内的N个不重复的随机数
Mar 18 PHP
基于php无限分类的深入理解
Jun 02 #PHP
php curl的深入解析
Jun 02 #PHP
Window 7/XP 安装Apache 2.4与PHP 5.4 的过程详解
Jun 02 #PHP
web站点获取用户IP的安全方法 HTTP_X_FORWARDED_FOR检验
Jun 01 #PHP
获取用户Ip地址通用方法与常见安全隐患(HTTP_X_FORWARDED_FOR)
Jun 01 #PHP
php源代码安装常见错误与解决办法分享
May 28 #PHP
如何批量替换相对地址为绝对地址(利用bat批处理实现)
May 27 #PHP
You might like
第一节--面向对象编程
2006/11/16 PHP
php 接口类与抽象类的实际作用
2009/11/26 PHP
探讨多键值cookie(php中cookie存取数组)的详解
2013/06/06 PHP
ThinkPHP实现非标准名称数据表快速创建模型的方法
2014/11/29 PHP
PHP+Apache+Mysql环境搭建教程
2016/08/01 PHP
php实现的顺序线性表示例
2019/05/04 PHP
PHP 构造函数和析构函数原理与用法分析
2020/04/21 PHP
jsvascript图像处理—(计算机视觉应用)图像金字塔
2013/01/15 Javascript
七个很有意思的PHP函数
2014/05/12 Javascript
jQuery实现仿新浪微博浮动的消息提示框(可智能定位)
2015/10/10 Javascript
浅谈移动端之js touch事件 手势滑动事件
2016/11/07 Javascript
浅谈webpack 构建性能优化策略小结
2018/06/13 Javascript
浅谈Vue组件及组件的注册方法
2018/08/24 Javascript
对angularjs框架下controller间的传值方法详解
2018/10/08 Javascript
JS实现普通轮播图特效
2020/01/01 Javascript
vue打包静态资源后显示空白及static文件路径报错的解决
2020/09/02 Javascript
vue 实现基础组件的自动化全局注册
2020/12/25 Vue.js
详解ES6 中的Object.assign()的用法实例代码
2021/01/11 Javascript
[03:52]DOTA2英雄基础教程 酒仙
2013/12/23 DOTA
Python函数式编程指南(三):迭代器详解
2015/06/24 Python
Python基于回溯法子集树模板解决选排问题示例
2017/09/07 Python
python opencv之SIFT算法示例
2018/02/24 Python
Django中自定义查询对象的具体使用
2019/10/13 Python
python实现大量图片重命名
2020/03/23 Python
python 基于dlib库的人脸检测的实现
2019/11/08 Python
python生成13位或16位时间戳以及反向解析时间戳的实例
2020/03/03 Python
Python实现疫情通定时自动填写功能(附代码)
2020/05/27 Python
Python numpy矩阵处理运算工具用法汇总
2020/07/13 Python
使用Python将语音转换为文本的方法
2020/08/10 Python
html5嵌入内容_动力节点Java学院整理
2017/07/07 HTML / CSS
Mio Skincare英国官网:身体紧致及孕期身体护理
2018/08/19 全球购物
德国受欢迎的旅游和休闲网站:lastminute.de
2019/09/23 全球购物
刑事辩护授权委托书
2014/09/13 职场文书
公安交警个人对照检查材料思想汇报
2014/10/01 职场文书
优秀班主任主要事迹材料
2014/12/16 职场文书
研究生学习计划书应该怎么写?
2019/09/10 职场文书