- 浏览: 12627 次
- 性别:
- 来自: 厦门
最新评论
<?php /** * 树形结构操作模型:改进的前序遍历 —— 表必备字段: id,name,lft,rgt,nlevel */ class TreeModel extends Model { protected $_validate = array( array('name', '2,32', '字符长度需在2到32字符之内', Model::MUST_VALIDATE, 'length'), ); /** * 判断表中是否有根节点 * @return int */ function isHasRoot() { return $this->where('lft=1 and nlevel=0')->getField('id'); } /** * 判断名字是否已存在 * @param string $name * @return int */ function isExistName($name) { return $this->where( array('name'=>$name) )->getField('id'); } /** * 判断节点是否为指定节点的子节点 * @param int $childId: 子节点 * @param int $parentId: 父节点 * @return bool */ function isChild($childId, $parentId) { if ( $childId == $parentId ) { return false; } $nodes = $this->whereNode( array($childId, $parentId) )->getField('id,lft,rgt'); $child = $nodes[$childId]; $parent = $nodes[$parentId]; if ( $child && $parent ) { return ($child['lft'] > $parent['lft']) && ($child['rgt'] < $parent['rgt']); } return false; } /** * 获取节点信息 * @param int $nodeId * @return array */ function getNodeInfo($nodeId) { return $this->whereNode($nodeId)->field('id,lft,rgt,nlevel')->find(); } //---------------------------------where条件 代替where()---------------------------------------- /** * 条件:节点 * @param int|array $nodeId * @param array $conditions: 附加条件 * @return this */ function whereNode($nodeId, array $conditions=array()) { $nodeId = (array)$nodeId; $conditions['id'] = array('in', $nodeId); return $this->where( $conditions ); } /** * 条件:获取节点(在指定的层级范围内)的子节点 * @param array $node * @param int $childLevel: 距当前节点的层级, 为0时获取所有 * @param array $conditions: 附加条件 * @param bool $onlyLeaf: 为true时只获取叶子节点 * @return this */ function whereChild(array $node, $childLevel=1, array $conditions=array(), $onlyLeaf=false) { $conditions['lft'] = array('gt', $node['lft']); $conditions['rgt'][] = array('lt', $node['rgt']); if ( $onlyLeaf ) { $conditions['rgt'][] = array('eq', 'lft+1'); } if ( $childLevel>0 ) { $conditions['nlevel'] = array( array('gt', $node['nlevel']), array('elt', $node['nlevel'] + $childLevel) ); } return $this->where($conditions); } /** * 条件:获取节点(在指定的层级范围内)的(直系)祖先节点 * @param array $node: 节点信息 * @param int $parentLevel:距当前节点的层级,0为获取所有 * @param array $conditions: 附加条件 * @return array */ function whereParent(array $node, $parentLevel=1, array $conditions=array()) { //lft<$lft and rgt>$rgt $conditions['lft'] = array('lt', $node['lft']); $conditions['rgt'] = array('gt', $node['rgt']); if ( $parentLevel>0 ) { $conditions['nlevel'] = array( array('lt', $node['nlevel']), array('egt', $node['nlevel'] - $parentLevel) ); } return $this->where($conditions); } /** * 条件:获取节点排除指定节点后的子孙节点(即从node的子孙节点中将out节点及其子节点排除) * @param array $node: * @param array $out: 排除的节点 * @param array $conditions: 附加条件 * @return this */ function whereChildWithout(array $node, array $out, array $conditions=array()) { $conditions['lft'] = array('gt', $node['lft']); $conditions['rgt'] = array('lt', $node['rgt']); $conditions['_string'] = 'lft<'.$out['lft'].' or rgt>'.$out['rgt']; return $this->where($conditions); } //------------------------------------append---------------------------------------- /** * 添加节点 * @param array $data: 要添加的节点信息 * @param int $noteId: 节点id, 为0时添加首节点 * @param bool $insertToRight: 添加在目标节点的最右,为false时添加在最左 * @return int */ function appendNode(array $data, $nodeId, $insertToRight=true) { if ( !$this->create( $data ) ) { return false; } if ( !$nodeId ) { return $this->appendRootNode($data); } $node = $this->getNodeInfo($nodeId); if ( $node ) { $this->startTrans(); if ( $insertToRight ) { $id = $this->appendRightNode($data, $node); } else { $id = $this->appendLeftNode($data, $node); } $id!==false ? $this->commit() : $this->rollback(); return $id; } return false; } /** * 添加根节点 * @param array $data: 要添加的节点信息 * @return int */ protected function appendRootNode(array $data) { if ( !$this->isHasRoot() ) { $data['lft'] = 1; $data['rgt'] = 2; $data['nlevel'] = 0; return $this->add($data); } return false; } /** * 添加左节点: 添加在目标节点的最左 * @param array $data: 要添加的节点信息 * @param array $node: 目标节点信息 * @return int */ protected function appendLeftNode(array $data, $node) { //所有左(右)值大于目标节点的左值的节点其右值+2(包含当前节点的子节点) $upLft = $this->where('lft>'.$node['lft'])->setInc('lft', 2); $upRgt = $this->where('rgt>'.$node['lft'])->setInc('rgt', 2); $data['lft'] = $node['lft'] + 1; //左值加1 $data['rgt'] = $node['lft'] + 2; $data['nlevel'] = $node['nlevel'] + 1; $id = $this->add($data); if ( $upLft !== false && $upRgt && $id ) { //可能无左节点的更新,但肯定有右节点的更新 return $id; } else { return false; } } /** * 添加右节点: 添加在目标节点的最右 * @param array $data: 要添加的节点信息 * @param array $node: 目标节点信息 * @return int */ protected function appendRightNode(array $data, $node) { //所有左值大于目标节点的右值的节点其右值+2(不包含当前节点的子节点) $upLft = $this->where('lft>'.$node['rgt'])->setInc('lft', 2); //左值全部+2 $upRgt = $this->where('rgt>='.$node['rgt'])->setInc('rgt', 2); //含当前节点的右值 $data['lft'] = $node['rgt']; //当前节点右值 $data['rgt'] = $node['rgt'] + 1; $data['nlevel'] = $node['nlevel'] + 1; $id = $this->add($data); if ( $upLft !== false && $upRgt && $id ) { return $id; } else { return false; } } //-----------------------------------remove----------------------------------------- /** * 删除节点 * @param int $nodeId * @param bool $isDelChild: 是否一起删除该节点下的子节点, 为false时节点下的子节点将置于该节点的父节点下 * @return bool */ function removeNode($nodeId, $isDelChilds=true) { $nodeInfo = $this->getNodeInfo($nodeId); if ( $nodeInfo && $nodeInfo['nlevel'] ) { //排除根节点 $this->startTrans(); if ($isDelChilds) { $del = $this->removeChildNodes($nodeInfo); } else { $del = $this->removeSingleNode($nodeInfo); } $del!==false ? $this->commit() : $this->rollback(); return $del; } return false; } /** * 删除单一节点,若此节点含有子节点,则将子节点转到此节点的父节点下 * @param array $nodeInfo:待删除的节点信息 * @return bool */ protected function removeSingleNode(array $nodeInfo) { //删除节点 $del = $this->where('id='.$nodeInfo['id'])->delete(); //更新节点的子节点:左右值都减1,层级减1 $sets = array( 'rgt' => array('exp', 'rgt - 1'), 'lft' => array('exp', 'lft - 1'), 'nlevel' => array('exp', 'nlevel - 1'), ); $upChild = $this->where('lft between '.$nodeInfo['lft'].' and '.$nodeInfo['rgt'])->setField($sets); //更新右值 $upRgt = $this->where('rgt>'.$nodeInfo['rgt'])->setDec('rgt', 2); //更新左值 $upLft = $this->where('lft>'.$nodeInfo['rgt'])->setDec('lft', 2); return $del && $upRgt && $upLft !== false && $upChild !== false; } /** * 删除节点及其子节点 * @param array $nodeInfo:待删除的节点信息 * @param bool */ protected function removeChildNodes(array $nodeInfo) { //删除节点及其子节点 $del = $this->where('lft between '.$nodeInfo['lft'].' and '.$nodeInfo['rgt'])->delete(); $diff = $nodeInfo['rgt'] - $nodeInfo['lft'] + 1; //节点差值 //更新右值 $upRgt = $this->where('rgt>'.$nodeInfo['rgt'])->setDec('rgt', $diff); //更新左值 $upLft = $this->where('lft>'.$nodeInfo['rgt'])->setDec('lft', $diff); return $del && $upRgt && $upLft !== false; } //-----------------------------------move------------------------------------------- /** * 节点移动:移动节点时该节点的子节点也会一起移动(禁止从父节点移动到子节点) * @param int $fromId: 待移动的节点 * @param int $toId: 移向的目的节点 * @return bool */ function moveTo($fromId, $toId) { if ( $fromId == $toId ) { //移到自身 $this->error = '父节点错误'; return false; } $nodes = $this->whereNode( array($fromId, $toId) )->getField('id,lft,rgt,nlevel'); $from = $nodes[$fromId]; $to = $nodes[$toId]; if ( !($from && $to) ) { $this->error = '未知节点'; return false; } else if ( $from['lft'] < $to['lft'] && $from['rgt'] > $to['rgt'] ) { //从父节点移到子节点 $this->error = '禁止从父节点移向子节点'; return false; } else if ( $from['nlevel'] == $to['nlevel']+1 && $to['lft'] < $from['lft'] && $to['rgt'] > $from['rgt'] ) { //本身即为父子节点 return true; } if ( $from['lft'] > $to['rgt'] ) { //左移 $move = $this->moveToLeft($from, $to); } else if ( $from['rgt'] < $to['lft'] ) { //右移 $move = $this->moveToRight($from, $to); } else { $move = $this->moveToParent($from, $to); //移向父节点 } $move!==false ? $this->commit() : $this->rollback(); return $move; } /** * 节点左移 * @param array $src: 要移动的节点信息 * @param array $to: 目标节点信息 * @return bool */ protected function moveToLeft(array $src, array $to) { $nodeStep = $src['rgt'] - $src['lft'] + 1; //-------------置于目标节点的最右-------------------- //更新中间节点的右值,此时中间节点的右值与待移节点的右值有重复 $upRgt = $this->where('rgt<'.$src['lft'].' and rgt>='.$to['rgt'])->setInc('rgt', $nodeStep); //包含目标节点 //更新移动节点的左右值(判断待移节点的左值), 此时与中间节点的左值有重复,右值无重复 $moveStep = $src['lft'] - $to['rgt']; $diffLevel = $to['nlevel'] - $src['nlevel'] + 1; //比目标等级低1 $sets = array( 'lft' => array('exp', 'lft-'.$moveStep), 'rgt' => array('exp', 'rgt-'.$moveStep), 'nlevel' => array('exp', 'nlevel+'.$diffLevel), ); $upNode = $this->where('lft between '.$src['lft'].' and '.$src['rgt'])->setField($sets); //更新中间节点的左值(判断中间节点的右值),$to['rgt']+$nodeStep为移动后的目标节点的右值 $wheres = 'lft<'.$src['lft'].' and lft>'.$to['lft'].' and rgt>'.($to['rgt']+$nodeStep); //左值更新范围(排序移动节点) $upLft = $this->where($wheres)->setInc('lft', $nodeStep); return $upNode && $upRgt && $upLft !== false; } /** * 节点右移 * @param array $from: 要移动的节点信息 * @param array $to: 目标节点信息 * @return bool */ protected function moveToRight(array $from, array $to) { $nodeStep = $from['rgt'] - $from['lft'] + 1; //-----------------置于目标节点的最左----------------- //更新中间节点的左值,此时中间节点的左值与待移节点的左值有重复 $upLft = $this->where('lft>'.$from['rgt'].' and lft<='.$to['lft'])->setDec('lft', $nodeStep); //包含目标节点 //更新移动节点的左右值(判断待移节点的右值), 此时与中间节点的右值有重复,左值无重复 $moveStep = $to['lft'] - $from['rgt']; $diffLevel = $to['nlevel'] - $from['nlevel'] + 1; //比目标等级低1 $sets = array( 'lft' => array('exp', 'lft+'.$moveStep), 'rgt' => array('exp', 'rgt+'.$moveStep), 'nlevel' => array('exp', 'nlevel+'.$diffLevel), ); $upNode = $this->where('rgt between '.$from['lft'].' and '.$from['rgt'])->setField($sets); //更新中间节点的右值(判断中间节点的左值),$to['lft']-$nodeStep为移动后的目标节点的左值 $wheres = 'rgt>'.$from['rgt'].' and rgt<'.$to['rgt'].' and lft<'.($to['lft']-$nodeStep); //右值更新范围(排除移动节点) $upLft = $this->where( $wheres )->setDec('rgt', $nodeStep); return $upNode && $upRgt && $upLft !== false; } /** * 节点上移 * @param array $from: 要移动的节点信息 * @param array $to: 目标节点信息 * @return bool */ protected function moveToParent(array $from, array $to) { $nodeStep = $from['rgt'] - $from['lft'] + 1; //-----------------置于目标节点的最右----------------- //更新中间节点的右值,此时中间节点的右值与待移节点的右值有重复 $upRgt = $this->where('rgt>'.$from['rgt'].' and rgt<'.$to['rgt'])->setDec('rgt', $nodeStep); //不包含目标节点 //更新移动节点的左右值(判断待移节点的右值), 此时与中间节点的左值有重复,右值无重复 $moveStep = $to['rgt'] - $from['rgt'] - 1; //本身就在$to节点下,故再减1 $diffLevel = $to['nlevel'] - $from['nlevel'] + 1; //比目标等级低1 $sets = array( 'lft' => array('exp', 'lft+'.$moveStep), 'rgt' => array('exp', 'rgt+'.$moveStep), 'nlevel' => array('exp', 'nlevel+'.$diffLevel), ); $upNode = $this->where('lft between '.$from['lft'].' and '.$from['rgt'])->setField($sets); //更新中间节点的左值(判断中间节点的右值),$to['rgt']-$nodeStep为排除移动节点 $wheres = 'lft>'.$from['lft'].' and rgt>'.$from['lft'].' and rgt<'.($to['rgt']-$nodeStep); //左值更新范围(排除移动节点) $upRgt = $this->where( $wheres )->setDec('lft', $nodeStep); return $upNode && $upRgt && $upLft !== false; } }
相关推荐
使用ThinkPHP框架实现的新闻无限级分类,非常实用
ThinkPHP 3.0正式版完全开发手册 CHM格式 如果下载解压后无显示,请单击右键->属性:单击常规标签下方的“解除锁定”,然后“应用”,“确定”,即可查看 -------------------- 华丽的分割线 ---------------------...
THINKPHP3.0执行流程图PDF,描述了thinkphp3.0的系统运行流程。
thinkphp3.0源码,可以做网站系统架构一款使用的MVC模式
thinkphp3.0源码和实例,是学习thinkphp的非常好的资源,
利用thinkphp3.0做的ajax评论
早期的思想架构来源于Struts,后来经过不断改进和完善,同时也借鉴了国外很多优秀的框架和模式,使用面向对象的开发结构和MVC模式,融合了Struts的Action和Dao思想和JSP的TagLib(标签库)、RoR的ORM映射和...
ThinkPHP3 0_完全开发手册
在此感谢ThinkPHP团队的所有成员和所有关注和支持ThinkPHP的朋友。 有关ThinkPHP项目及本文档的最新资料,请及时访问ThinkPHP项目主站 http://thinkphp.cn 。 本文档及其描述的内容受有关法律的版权保护,对本文档...
ThinkPHP3.0完整版和 完全开发手册
用ThinkPHP3.0写的博客程序,多图片上传,后台界面是山寨的phpcms。整体来说功能不错。是小弟在网上找到,在此贡献给大家
可外调 上传图片、FLASH、视音频、文件或浏览服务端的对应项。添加浏览时删除文件功能。 修改过kindeditor的filemanager.js,flash.js,image.js,insertfile.js,media.js,map。主要文件Public\js\kedit.js,WebApp\Lib\...
早期的思想架构来源于Struts,后来经过不断改进和完善,同时也借鉴了国外很多优秀的框架和模式,使用面向对象的开发结构和MVC模式,融合了Struts的Action和Dao思想和JSP的TagLib(标签库)、RoR的ORM映射和...
仅供测试使用,后果自行负责。
thinkphp-bjyblog, 基于thinkphp开发的的个人博客系统thinkphp-bjyblog
版本: ThinkPHP_3.0RC2_Core 作者: selfimpr Blog: http://blog.csdn.net/lgg201 Mail: lgg860911@yahoo.com.cn 环境/工具: • php-5.3.5-fpm • nginx-1.0.14 • vim + vim.debugger • xdebug-2.2.0RC1 参考: • ...
ThinkPHP_3.0_Full
插件下载解压得到cn-thinkphp-newone.nbm,然后安装就好。 我的开发运行环境:netbeans-6.5-ml-java-windows + netbeans-6.5-ml-php-windows + jdk-6u10-windows-i586-p + WampServer Version 2.0 大家测试插件的...
在该文件中完整的展示了如何利用tp的模型从mysql取得数据,并进行无限分类,最终转换成layui树形组件所需正确格式,当然也添加了一些layui所需的自定义属性(例如:title、spread等),几乎每一行都有注释,也表明了...
拍卖网pc-wap-thinkphp源码