解析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 相关文章推荐
php getsiteurl()函数
Sep 05 PHP
用PHP获取Google AJAX Search API 数据的代码
Mar 12 PHP
优化php效率,提高php性能的一些方法
Mar 24 PHP
解析PHP SPL标准库的用法(遍历目录,查找固定条件的文件)
Jun 18 PHP
php中让上传的文件大小在上传前就受限制的两种解决方法
Jun 24 PHP
php中的常用魔术方法总结
Aug 02 PHP
PHP中include与require使用方法区别详解
Oct 19 PHP
php简单的留言板与回复功能具体实现
Feb 19 PHP
php 使用file_get_contents读取大文件的方法
Nov 13 PHP
PHP中的替代语法介绍
Jan 09 PHP
PHP单态模式简单用法示例
Nov 16 PHP
PHP常见加密函数用法示例【crypt与md5】
Jan 27 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
ajax+php打造进度条 readyState各状态
2010/03/20 PHP
解析php curl_setopt 函数的相关应用及介绍
2013/06/17 PHP
php使用ICQ网关发送手机短信
2013/10/30 PHP
php发送短信验证码完成注册功能
2015/11/24 PHP
php强制下载文件函数
2016/08/24 PHP
thinkPHP5框架实现多数据库连接,跨数据连接查询操作示例
2019/05/29 PHP
BOOM vs RR BO5 第四场 2.14
2021/03/10 DOTA
JAVASCRIPT车架号识别/验证函数代码 汽车车架号验证程序
2012/01/08 Javascript
基于jQuery实现下拉收缩(展开与折叠)特效
2012/12/25 Javascript
JQuery入门——事件切换之hover()方法应用介绍
2013/02/05 Javascript
js substr支持中文截取函数代码(中文是双字节)
2013/04/17 Javascript
jQuery实现的多选框多级联动插件
2014/05/02 Javascript
AngularJS 简单应用实例
2016/07/28 Javascript
vue滚动轴插件better-scroll使用详解
2017/10/17 Javascript
vue中如何创建多个ueditor实例教程
2017/11/14 Javascript
windows系统下更新nodejs版本的方案
2017/11/24 NodeJs
vue项目中v-model父子组件通信的实现详解
2017/12/10 Javascript
nodejs实现截取上传视频中一帧作为预览图片
2017/12/10 NodeJs
js自定义trim函数实现删除两端空格功能
2018/02/09 Javascript
PostgreSQL Node.js实现函数计算方法示例
2019/02/12 Javascript
vue使用微信扫一扫功能的实现代码
2020/04/11 Javascript
Python线性回归实战分析
2018/02/01 Python
python绘制散点图并标记序号的方法
2018/12/11 Python
python3读取autocad图形文件.py实例
2020/06/05 Python
CSS3媒体查询Media Queries基础学习教程
2016/02/29 HTML / CSS
HTML5中使用postMessage实现Ajax跨域请求的方法
2016/04/19 HTML / CSS
意大利领先的线上奢侈品销售电商:Eleonora Bonucci
2017/10/17 全球购物
药剂学专业应届生自荐信
2013/09/29 职场文书
大一学生假期实习的自我评价
2013/10/12 职场文书
小学三年级数学教学反思
2014/01/31 职场文书
迟到检讨书500字
2014/02/05 职场文书
交通事故协议书范文
2014/10/23 职场文书
钳工实训报告总结
2014/11/04 职场文书
大学四年个人总结
2015/03/03 职场文书
幼儿园园务工作总结2015
2015/05/18 职场文书
预备党员考察意见范文
2015/06/01 职场文书