解析thinkphp的左右值无限分类


Posted in PHP onJune 20, 2013

以前一直使用父子无限分类,这种分类结构清晰,使用也简单。但若分类数量很大的话,在查询上性能不佳。比如在做导航菜单中,我要根据某一分类查询出整个分类树的话(祖辈)。
性能消耗是非常大的,要么做递归,要么做多次查询。故,对于分类的数据量很大的情况,我推荐使用左右值,以减少查询上的麻烦。

_id
    /**
         +----------------------------------------------------------
         * 构造函数
         * @access public
         * @return void
         +----------------------------------------------------------
         */
    public  function __construct($left,$right,$id){
        parent::__construct();
       $this->_left = $left;
       $this->_right = $right;
       $this->_id = $id;
    }
    /**
      +----------------------------------------------------------
      * 根据node$this->_id得到该node的所有值
      * @access public
      * @param $nodeId
      * @return array
      +----------------------------------------------------------
     */     
    public  function getNodeById($nodeId)
    {
        if($nodeId>0)
        {
            return $this->getById($nodeId);
        }
        else
        {
            throw_exception('未知$this->_id');
            return false;
        }
    }
    /**
           +----------------------------------------------------------
           * 获取父节点,含直属父类(type=1),所有父类:type=0
           * @access public 
           * @param $nodeId int 节点$this->_id
           * @return $parentNode array()
           +----------------------------------------------------------
          */     
    public  function getParentNode($nodeId,$type = 0)
    {
        if($nodeId == 0) throw_exception('未知$this->_id');;
        $currentNode = $this->getNodeById($nodeId);
        if($currentNode)
        {
            $condition = " ".$this->_left.'<'.$currentNode[$this->_left].' and '.$this->_right.' >'.$currentNode[$this->_right]." ";
            if($type ==1) //直属父类
            {
                return $this->where($condition)->order($this->_left." DESC")->limit(1)->find();
                //                $sql = "SELECT * FROM ".TABLE_NAME." WHERE {$condition} ORDER BY ".$this->_left." DESC LIMIT 1";
                //                return mysql_query($sql) or die(mysql_error());
            }
            else if($type ==0)
            {
                return $this->where($condition)->findAll();
                //                $sql = "SELECT * FROM ".TABLE_NAME." WHERE {$condition} ";
                //                return mysql_query($sql) or die(mysql_error());
            }
        }
        else
        {
            return false;
        }
    }
    /**
         +----------------------------------------------------------
         * 当前节点下子孙节点总数.子孙总数=(当前节点的右值 - 当前节点的左值-1)/2
         * @access public 
         * @param $node_id int 节点$this->_id
         * @return $amount int 该节点下的子孙总数         * 
         +----------------------------------------------------------
         */
    public  function getChildCount($nodeId)
    {
        $currentNode = $this->getNodeById($nodeId);
        if(!empty($currentNode))
        {
            return (int)($currentNode[$this->_right]-$currentNode[$this->_left] -1)/2;
        }
    }
    /**
      +----------------------------------------------------------
      * 获取当前节点下所有子节点。 当 A子类的右节点=B子类左节点-1 则 A、B属于同一级别
      * @access public 
      * @param $curentId
      * @param  $type int 0:当前节点下所有子类,1为当前节点下一级子类
      * @return bool
      +----------------------------------------------------------
     */     
    public  function getChild($nodeId,$type=0)
    {
        $currentNode = $this->getNodeById($nodeId);
        if($currentNode[$this->_left]-$currentNode[$this->_right] ==1)
        {
            return false; //当 该节点左值 - 右值=1  时,其下没有子节点。
        }
        else
        {
            $condition = $this->_left.'>'.$currentNode[$this->_left].' and '.$this->_right .'<'.$currentNode[$this->_right];
            $child = $this->where($condition)->findAll();
            if($type == 0)//所有子类
            {
                return $child;
            }
            else if($type ==1) //获取当前节点下一级分类
            {                        
                $subArr = array(); //一级子类
                foreach ($child as $k=>$sub) {
                    //子类的左节点=父类左节点+1,则子类为第一个子类
                    if($sub[$this->_left]==$currentNode[$this->_left]+1)
                    {
                        //$right = $sub[$k][$this->_right]; //当前节点的右节点
                        $firstSub = $sub; //当前节点下第一个子类
                        array_push($subArr,$firstSub); //子类入栈
                        unset($child[$k]);
                    }
                }
                $rightVal =  $firstSub[$this->_right]; //第一个子节点为比较标志
                $childCount = count($child);//剩余子节点数
                for($i=0;$i<$childCount;$i++) //循环检索出 同级子节点
                {
                    foreach ($child as $key => $sub2) {
                        if($rightVal == $sub2[$this->_left]-1)
                        {
                            $rightVal = $sub2[$this->_right]; //把循环当前的node的右节点当做比较值
                            array_push($subArr,$sub2);
                            unset($child[$key]);
                        }
                    }
                }
                return $subArr;
            }
        }
    }
    /**
         +----------------------------------------------------------
         * 返回当前节点的完整路径
         * @access public 
         * @param $nodeId
         * @return array
         +----------------------------------------------------------
        */     
    public  function getSinglePath($nodeId)
    {
        $sql = "select parent.* from __TABLE__ as node,__TABLE__ as parent where node.{$this->_left} between parent.{$this->_left}
            AND parent.{$this->_right} AND node.{$this->_id} = {$nodeId} order by parent.{$this->_left}";
//        echo $sql;
        return $this->query($sql);
    }
    /**
      +----------------------------------------------------------
      * 添加子节点,分3种:0:在当前节点下最后追加一个子节点;1:在当前节点下追加第一个子节点;

2:在当前节点下的某个子节点后追加
      * @access public 
      * @param $currentId int 
      * @param $nodeName string 新节点名称      
      * @param $targetId int 追加到当前节点下子节点的指定节点后
      * @return bool
      +----------------------------------------------------------
     */    
    public  function addNode($nodeId,$newData,$type=0,$targetId=0)
    {
        if(empty($newData))
        {
            throw_exception('新分类不能为空');
        }
        $currentNode = $this->getNodeById($nodeId);
        switch ($type) {
            case 0:
                $leftNode  = $currentNode[$this->_right]; //新节点的左值为父节点的右值
                $rightNode = $leftNode+1;
                break;
            case 1:
                $leftNode = $currentNode[$this->_left]+1; //新节点的左值为父节点的左值+1
                $rightNode = $leftNode+1;
                break;
            case 2:
                $otherNode = $this->getNodeById($targetId);
                $leftNode = $otherNode[$this->_right]+1;
                $rightNode = $leftNode+1;
            default:
                break;
        }
//         $sql = "UPDATE ".TABLE_NAME." SET ".$this->_right."=".$this->_right."+2 WHERE ".$this->_right." >= ".$leftNode;
//        $sql2 = "UPDATE ".TABLE_NAME." SET ".$this->_left."=".$this->_left."+2 WHERE ".$this->_left.">".$leftNode;
        $this->setInc($this->_right,$this->_right.">=".$leftNode,2); //把所有右值大于新节点左值的节点的右值+2,注意效率
        $this->setInc($this->_left,$this->_left.">".$leftNode,2);   //把所有大于新节点的左值+2
        $newData[$this->_left] = (int)$leftNode;
        $newData[$this->_right] =(int) $rightNode;
        return $this->add($newData);
    }
    /**
         +----------------------------------------------------------
         * 删除节点
         * @access public 
         * @param type 操作类型,默认为0删除当前节点下的所有子节点,1为删除包括自身的节点
         * @param $nodeId int 要删除的$this->_id
         * @return bool
         +----------------------------------------------------------
        */     
    public  function rmNode($nodeId,$type =1)
    {
        $currentNode = $this->getNodeById($nodeId);
        if($type == 1) //删除包含自身的节点
        {
            $sql = "DELETE FROM __TABLE__ WHERE ".$this->_left.">= {$currentNode[$this->_left]} AND ".$this->_right."<= {$currentNode[$this->_right]}";
            $childCount = ($this->getChildCount($nodeId)+1)*2; //要更新的值
            $sql2 = "UPDATE  __TABLE__  SET ".$this->_right."=".$this->_right."-".$childCount." WHERE ".$this->_right.">".$currentNode[$this->_right];
            $sql3 = "UPDATE  __TABLE__  SET ".$this->_left."=".$this->_left."-".$childCount." WHERE ".$this->_left.">".$currentNode[$this->_left];
        }
        else //删除当前节点下的所有节点
        {
            $sql ="DELETE FROM __TABLE__ WHERE ".$this->_left."> {$currentNode[$this->_left]} AND ".$this->_right."< {$currentNode[$this->_right]}";
            $childCount = $this->getChildCount($nodeId)*2; //要更新的值
            $sql2 = "UPDATE __TABLE__ SET ".$this->_right."=".$this->_right ."-".$childCount." WHERE ".$this->_right.">=".$currentNode[$this->_right];
            $sql3 = "UPDATE __TABLE__ SET ".$this->_left."=".$this->_left."-".$childCount." WHERE ".$this->_left.">".$currentNode[$this->_left];
        }
         $this->execute($sql);  
         $this->execute($sql2);  
         $this->execute($sql3);  
        return true;
    }
     /**
      +----------------------------------------------------------
      * 修改节点,名称等
      * @access public 
      * @param $newData array()必须含有 要修改的$this->_id,k-v必须对齐,如arr['node_name'] = '商品'
      * @return bool
      +----------------------------------------------------------
     */     
    public  function modiNode($newData)
       {
            if(!empty($newData))
            {
                $id = $newData[$this->_id];                
                unset($newData[$this->_id]);
                return $this->save($newData,$this->_id.'='.$id);                               
          }
       }
}
?>

PHP 相关文章推荐
Classes and Objects in PHP5-面向对象编程 [1]
Oct 09 PHP
一周学会PHP(视频)Http下载
Dec 12 PHP
PHP 实现多服务器共享 SESSION 数据
Aug 15 PHP
php xml留言板 xml存储数据的简单例子
Aug 24 PHP
THINKPHP+JS实现缩放图片式截图的实现
Mar 07 PHP
使用PHP获取当前url路径的函数以及服务器变量
Jun 29 PHP
php无限极分类递归排序实现方法
Nov 11 PHP
php计算给定时间之前的函数用法实例
Apr 03 PHP
PHP的Yii框架中移除组件所绑定的行为的方法
Mar 18 PHP
PHP实现的策略模式简单示例
Aug 25 PHP
CI框架教程之优化验证码机制详解【验证码辅助函数】
Apr 16 PHP
php tpl模板引擎定义与使用示例
Aug 09 PHP
PHP 清空varnish 缓存的详解(包括指定站点下的)
Jun 20 #PHP
PHP array_multisort() 函数的深入解析
Jun 20 #PHP
PHP操作MongoDB GridFS 存储文件的详解
Jun 20 #PHP
解析Linux下Varnish缓存的配置优化
Jun 20 #PHP
解析PHP中常见的mongodb查询操作
Jun 20 #PHP
PHP 解决session死锁的方法
Jun 20 #PHP
解析PHP可变函数的经典用法
Jun 20 #PHP
You might like
php递归json类实例
2014/12/02 PHP
php根据日期显示所在星座的方法
2015/07/13 PHP
tp5框架基于ajax实现异步删除图片的方法示例
2020/02/10 PHP
laravel中Redis队列监听中断的分析
2020/09/14 PHP
OfflineSave离线保存代码再次发布使用说明
2007/05/23 Javascript
JQuery 表单中textarea字数限制实现代码
2009/12/07 Javascript
Jquery练习之表单验证实现代码
2010/12/14 Javascript
JavaScript高级程序设计(第3版)学习笔记3 js简单数据类型
2012/10/11 Javascript
javascript+xml实现简单图片轮换(只支持IE)
2012/12/23 Javascript
jQuery实现用户注册的表单验证示例
2013/08/28 Javascript
提高NodeJS中SSL服务的性能
2014/07/15 NodeJs
JQuery中DOM事件绑定用法详解
2015/06/13 Javascript
jQuery绑定事件监听bind和移除事件监听unbind用法实例详解
2016/01/19 Javascript
浅谈JS继承_寄生式继承 &amp; 寄生组合式继承
2016/08/16 Javascript
jQuery  ready方法实现原理详解
2016/10/19 Javascript
微信小程序本作用域下调用全局JS详解及实例
2017/02/22 Javascript
js实现鼠标拖动功能
2017/03/20 Javascript
Bootstrap模态框插件使用详解
2017/05/11 Javascript
jQuery自定义多选下拉框效果
2017/06/19 jQuery
vue data对象重新赋值无效(未更改)的解决方式
2020/07/24 Javascript
vue中的v-model原理,与组件自定义v-model详解
2020/08/04 Javascript
原生js实现弹幕效果
2020/11/29 Javascript
sublime text 3配置使用python操作方法
2017/06/11 Python
简单了解什么是神经网络
2017/12/23 Python
使用Python爬虫库requests发送请求、传递URL参数、定制headers
2020/01/25 Python
python实现学生通讯录管理系统
2021/02/25 Python
五分钟学会HTML5的WebSocket协议
2019/11/22 HTML / CSS
中科软测试工程师面试题
2012/06/16 面试题
写一个函数,要求输入一个字符串和一个字符长度,对该字符串进行分隔
2015/07/30 面试题
《孙权劝学》教学反思
2014/04/23 职场文书
治超工作实施方案
2014/05/04 职场文书
五一劳动节慰问信
2015/02/14 职场文书
2016年质量月活动总结报告
2016/04/05 职场文书
python将图片转为矢量图的方法步骤
2021/03/30 Python
JavaScript如何利用Promise控制并发请求个数
2021/05/14 Javascript
Redis 哨兵机制及配置实现
2022/03/25 Redis