解析thinkphp的左右值无限分类
2015-01-24信息快讯网
本篇文章是对thinkphp的左右值无限分类进行了详细的分析介绍,需要的朋友参考下
以前一直使用父子无限分类,这种分类结构清晰,使用也简单。但若分类数量很大的话,在查询上性能不佳。比如在做导航菜单中,我要根据某一分类查询出整个分类树的话(祖辈)。性能消耗是非常大的,要么做递归,要么做多次查询。故,对于分类的数据量很大的情况,我推荐使用左右值,以减少查询上的麻烦。
_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中的路径问题与set_include_path使用介绍
thinkphp3查询mssql数据库乱码解决方法分享
修改apache配置文件去除thinkphp url中的index.php
php获取淘宝分类id示例
那些年我们错过的魔术方法(Magic Methods)
浅析ThinkPHP中的pathinfo模式和URL重写
thinkphp的CURD和查询方式介绍
php ZipArchive压缩函数详解实例
php根据分类合并数组的方法实例详解
PHP的构造方法,析构方法和this关键字详细介绍
PHP无限分类(树形类)
php 地区分类排序算法
解析PHP无限级分类方法及代码
解析左右值无限分类的实现算法
PHP 清空varnish 缓存的详解(包括指定站点下的)
解析thinkphp中的导入文件标签
解析thinkphp import 文件内容变量失效的问题
解析使用ThinkPHP应该掌握的调试手段
php无限分类且支持输出树状图的详细介绍
解析thinkphp基本配置 convention.php
thinkphp中连接oracle时封装方法无法用的解决办法
利用php递归实现无限分类 格式化数组的详解
PHP无限分类(树形类)的深入分析
基于php无限分类的深入理解
php无限极分类实现的两种解决方法