PHP带节点操作的无限分类实现方法详解


Posted in PHP onNovember 09, 2016

本文实例讲述了PHP带节点操作的无限分类实现方法。分享给大家供大家参考,具体如下:

包含(移动多个节点;移动单个节点;删除多个节点;删除单个节点;新增节点),另附数据库表结构

一、db sql语句

//db used for php无限分类
create table tree(
  id int(10) not null primary key auto_increment,
  name varchar(255) not null,
  lft int(10) not null default 0,
  rgt int(10) not null default 0,
  status int(1) not null default 0,
  index lft (`lft`),
  index rgt (`rgt`),
  index status(`status`)
)charset utf8;
insert into tree value (null,'Food',1,18,0);
insert into tree value (null,'Fruit',2,11,0);
insert into tree value (null,'Red',3,6,0);
insert into tree value (null,'Cherry',4,5,0);
insert into tree value (null,'Yellow',7,10,0);
insert into tree value (null,'Banana',8,9,0);
insert into tree value (null,'Meat',12,17,0);
insert into tree value (null,'Beef',13,14,0);
insert into tree value (null,'Pork',15,16,0);

二、php文件

<?php
error_reporting(0);
/*
             1 Food 18
      +------------------------------+
    2 Fruit 11           12 Meat 17
  +-------------+         +------------+
3 Red 6   7 Yellow 10    13 Beef 14  15 Pork 16
4 Cherry 5  8 Banana 9
descendants = (right ? left - 1) / 2
*/
/**
 *用于移动一个节点(包括子节点)
 *@param array $pdata = array('id'=>主键,'root'=>名称) 二选一 父节点(为空时插入最大的父节点)
 *@param array $ndata = array('id'=>主键,'root'=>名称) 二选一 下一个兄弟节点(没有兄弟的时候就不用)
 *@param array $cdata = array('id'=>主键,'root'=>名称) 二选一 当前待移动的节点
 */
function move_tree_all($pdata=array(),$ndata=array(),$cdata=array()) {
  $cid  = $cdata['id'] ? intval($cdata['id']) : '';
  $croot = $cdata['root'];
  if(!$cid && !$croot) return;
  //需自加判断
  //1、cdata不能为顶级
  //2、cdata不能比$pdata等级高
  $adata = get_tree_all($cdata); //获取当前移动节点的所有节点
  delete_tree_all($cdata,1); //逻辑删除当前移动节点的所有节点
  foreach($adata as $k => $val) {
    if($k != 0) {
      $pdata = array('root'=>$val['parent']);
      insert_tree($pdata,'',$val['name'],1);
    } else { //first
      insert_tree($pdata,$ndata,$val['name'],1);
    }
  }
}
/**
 *用于移动一个节点(不包括子节点)
 *@param array $pdata = array('id'=>主键,'root'=>名称) 二选一 父节点(为空时插入最大的父节点)
 *@param array $ndata = array('id'=>主键,'root'=>名称) 二选一 下一个兄弟节点(没有兄弟的时候就不用)
 *@param array $cdata = array('id'=>主键,'root'=>名称) 二选一 当前待移动的节点
 */
function move_tree_item($pdata=array(),$ndata=array(),$cdata=array()) {
  $cid  = $cdata['id'] ? intval($cdata['id']) : '';
  $croot = $cdata['root'];
  if(!$cid && !$croot) return;
  //需自加判断
  //1、cdata不能为顶级
  if(!$croot) {
    $sql = "SELECT name from tree where id = $cid";
    $result = mysql_query($sql);
    $row = mysql_fetch_assoc($result);
    $croot = $row['name'];
    unset($sql);
  }
  delete_tree_item($cdata,1);
  insert_tree($pdata,$ndata,$croot,1);
}
/**
 *用于插入一个节点
 *@param array $pdata = array('id'=>主键,'root'=>名称) 二选一 父节点(为空时插入最大的父节点)
 *@param array $ndata = array('id'=>主键,'root'=>名称) 二选一 下一个兄弟节点(没有兄弟的时候就不用)
 *@param string $name string 新插入的名称
 *@param int $update 默认为空,为1时更新插入
 */
function insert_tree($pdata=array(),$ndata=array(),$name,$update='') {
  if(!$name) return;
  $pid  = $pdata['id'] ? intval($pdata['id']) : '';
  $proot = $pdata['root'];
  $nid  = $ndata['id'] ? intval($ndata['id']) : '';
  $nroot = $ndata['root'];
  //有父无兄(最小的子节点,父节点的最后一个儿子)
  if(($pid || $proot) && !($nid || $nroot)) {
    $sql  = $pid ? "SELECT lft, rgt FROM tree WHERE id = '{$pid}';" : "SELECT lft, rgt FROM tree WHERE name = '{$proot}';";
    $result = mysql_query($sql);
    $row  = mysql_fetch_assoc($result);
    unset($sql);
    //新节点
    $lft = $row['rgt'];
    $rgt = $lft+1;
    if(!$update) {
      $sql = "insert into tree values (null,'{$name}',$lft,$rgt,0);";
      $sql1 = "update tree set rgt = rgt+2 where rgt >= {$row['rgt']}";
      $sql2 = "update tree set lft = lft+2 where lft >= {$row['rgt']}";
    } else {
      $sql = "update tree set lft=$lft,rgt=$rgt,status=0 where name ='{$name}';";
      $sql1 = "update tree set rgt = rgt+2 where status =0 and rgt >= {$row['rgt']}";
      $sql2 = "update tree set lft = lft+2 where status =0 and lft >= {$row['rgt']}";
    }
    mysql_query($sql1);
    mysql_query($sql2);
    mysql_query($sql); //last add new data
  }
  //有父有兄
  if(($pid || $proot) && ($nid || $nroot)) {
    $sql  = $nid ? "SELECT lft, rgt FROM tree WHERE id = '{$nid}';" : "SELECT lft, rgt FROM tree WHERE name = '{$nroot}';";
    $result = mysql_query($sql);
    $row  = mysql_fetch_assoc($result);
    unset($sql);
    //新节点
    $lft = $row['lft'];
    $rgt = $lft+1;
    if(!$update) {
      $sql = "insert into tree values (null,'{$name}',$lft,$rgt,0);";
      $sql1 = "update tree set rgt = rgt+2 where rgt >= {$row['lft']};";
      $sql2 = "update tree set lft = lft+2 where lft >= {$row['lft']};";
    } else {
      $sql = "update tree set lft=$lft,rgt=$rgt,status=0 where name ='{$name}';";
      $sql1 = "update tree set rgt = rgt+2 where status = 0 and rgt >= {$row['lft']};";
      $sql2 = "update tree set lft = lft+2 where status = 0 and lft >= {$row['lft']};";
    }
    mysql_query($sql1);
    mysql_query($sql2);
    mysql_query($sql); //last add new data
  }
  //无父无兄(大佬)
  if(!($pid || $proot) && !($nid || $nroot)) {
    $sql  = "SELECT max(`rgt`) as rgt FROM tree;";
    $result = mysql_query($sql);
    $row  = mysql_fetch_assoc($result);
    unset($sql);
    //新节点
    $lft = 1;
    $rgt = $row['rgt']+2;
    if(!$update) {
      $sql = "insert into tree values (null,'{$name}',$lft,$rgt,0);";
      $sql1 = "update tree set rgt = rgt+1";
      $sql2 = "update tree set lft = lft+1";
    } else {
      $sql = "update tree set lft=$lft,rgt=$rgt,status=0 where name ='{$name}';";
      $sql1 = "update tree set rgt = rgt+1 where status = 0";
      $sql2 = "update tree set lft = lft+1 where status = 0";
    }
    mysql_query($sql1);
    mysql_query($sql2);
    mysql_query($sql); //last add new data
  }
}
/**
 *用于删除一个节点(包括子节点)
 *@param array $data = array('id'=>主键,'root'=>名称) 二选一
 *@param int $update 默认为空,为1时逻辑删除
 */
function delete_tree_all($data,$update='') {
  $id  = $data['id'] ? intval($data['id']) : '';
  $root = $data['root'];
  if(!$id && !$root) return;
  $sql  = $id ? "SELECT lft, rgt FROM tree WHERE id = '{$id}';" : "SELECT lft, rgt FROM tree WHERE name = '{$root}';";
  $result = mysql_query($sql);
  $row  = mysql_fetch_assoc($result);
  unset($sql);
  $middle = $row['rgt']-$row['lft']+1;
  if(!$update) {
    $sql  = "delete from tree where lft BETWEEN '" . $row['lft'] . "' AND '" . $row['rgt'] ."'";
    $sql1  = "update tree set rgt = rgt-{$middle} where rgt > {$row['rgt']}";
    $sql2  = "update tree set lft = lft-{$middle} where lft > {$row['rgt']}";
  } else {
    $sql  = "update tree set status = 1 where lft BETWEEN '" . $row['lft'] . "' AND '" . $row['rgt'] ."'";
    $sql1  = "update tree set rgt = rgt-{$middle} where status=0 and rgt > {$row['rgt']}";
    $sql2  = "update tree set lft = lft-{$middle} where status=0 and lft > {$row['rgt']}";
  }
  mysql_query($sql);
  mysql_query($sql1);
  mysql_query($sql2);
}
/**
 *用于删除一个节点(不包括子节点)
 *@param array $data = array('id'=>主键,'root'=>名称) 二选一
 *@param int $update 默认为空,为1时逻辑删除
 */
function delete_tree_item($data,$update='') {
  $id  = $data['id'] ? intval($data['id']) : '';
  $root = $data['root'];
  if(!$id && !$root) return;
  $sql  = $id ? "SELECT id,lft, rgt FROM tree WHERE id = '{$id}';" : "SELECT id,lft, rgt FROM tree WHERE name = '{$root}';";
  $result = mysql_query($sql);
  $row  = mysql_fetch_assoc($result);
  unset($sql);
  if(!$update) {
    $sql = "delete from tree where id = {$row['id']};";
    $sql1 = "update tree set rgt = rgt-1,lft = lft -1 where lft > {$row['lft']} and rgt < {$row['rgt']}";
    $sql2 = "update tree set lft = lft-2 where lft > {$row['rgt']}";
    $sql3 = "update tree set rgt = rgt-2 where rgt > {$row['rgt']}";
  } else {
    $sql = "update tree set status = 1 where id = {$row['id']};";
    $sql1 = "update tree set rgt = rgt-1,lft = lft -1 where status = 0 and lft > {$row['lft']} and rgt < {$row['rgt']}";
    $sql2 = "update tree set lft = lft-2 where status = 0 and lft > {$row['rgt']}";
    $sql3 = "update tree set rgt = rgt-2 where status = 0 and rgt > {$row['rgt']}";
  }
  mysql_query($sql);
  mysql_query($sql1);
  //can do or not do just right,but not do load empty 2 number in middle
  mysql_query($sql2);
  mysql_query($sql3);
}
/**
 *用于获取所有的节点
 *@param array $data = array('id'=>主键,'root'=>名称) 二选一
 */
function get_tree_all($data) {
  $id  = $data['id'] ? intval($data['id']) : '';
  $root = $data['root'];
  if(!$id && !$root) return;
  $sql  = $id ? "SELECT lft, rgt FROM tree WHERE id = '{$id}';" : "SELECT lft, rgt FROM tree WHERE name = '{$root}';";
  $result = mysql_query($sql);
  $row  = mysql_fetch_assoc($result);
  $adata = array(); //所有数据
  $right = array(); //计数
  $prev  = array();
  $result = mysql_query("SELECT id,name, lft, rgt FROM tree WHERE lft BETWEEN '" . $row['lft'] . "' AND '" . $row['rgt'] ."' ORDER BY lft ASC ;");
  while ($row = mysql_fetch_assoc($result)) {
    if (count($right) > 0) {
      while ($right[count($right) - 1] < $row['rgt']) { // 检查我们是否应该将节点移出堆栈
        array_pop($right);
        array_pop($prev);
      }
    }
    $parent = $prev ? end($prev) : '';
    $adata[] = array('id'=>$row['id'],'name'=>$row['name'],'level'=>count($right),'parent'=>$parent);
    $right[] = $row['rgt'];
    $prev[] = $row['name'];
  }
  return $adata;
}
/**
 *用于展示分类
 *@param array $data = array('id'=>主键,'root'=>名称) 二选一
 */
function display_tree($data) {
  $id  = $data['id'] ? intval($data['id']) : '';
  $root = $data['root'];
  if(!$id && !$root) return;
  $sql  = $id ? "SELECT lft, rgt FROM tree WHERE id = '{$id}';" : "SELECT lft, rgt FROM tree WHERE name = '{$root}';";
  $result = mysql_query($sql);
  $row  = mysql_fetch_assoc($result);
  $right = array();
  $result = mysql_query("SELECT name, lft, rgt FROM tree WHERE lft BETWEEN '" . $row['lft'] . "' AND '" . $row['rgt'] ."' ORDER BY lft ASC ;");
  while ($row = mysql_fetch_assoc($result)) {
    if (count($right) > 0) { // 检查我们是否应该将节点移出堆栈
      while ($right[count($right) - 1] < $row['rgt']) {
        array_pop($right);
      }
    }
    echo str_repeat(' ',count($right)) . $row['name'] . "\n";
    $right[] = $row['rgt'];
  }
}
mysql_connect('localhost','root','') or die('connect error');
mysql_select_db('test') or die('database error');
mysql_query('set names utf8');
display_tree(array('root'=>'Food'));
//display_tree(array('root'=>'bigboss'));
//move_tree_all($pdata=array('root'=>'Fruit'),$ndata=array('root'=>'Red'),$cdata=array('root'=>'Meat'));
//move_tree_all('','',$cdata=array('root'=>'Meat'));
//move_tree_item('','',array('root'=>'Red'));
//move_tree_item(array('root'=>'Red'),array('root'=>'Cherry'),array('root'=>'Fruit'));
//delete_tree_all(array('root'=>'Yellow'));
//delete_tree_all(array('root'=>'Meat'));
//delete_tree_item(array('root'=>'Meat'));
//insert_tree('','','bigboss');
//insert_tree(array('root'=>'Red'),'','dalao');
//insert_tree(array('root'=>'Red'),array('root'=>'Cherry'),'baddalao');
//insert_tree(array('root'=>'Fruit'),array('root'=>'Red'),'Redbother');
display_tree(array('root'=>'Food'));

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
在线短消息收发的程序,不用数据库
Oct 09 PHP
PHP完整的日历类(CLASS)
Nov 27 PHP
如何使用“PHP” 彩蛋进行敏感信息获取
Aug 07 PHP
PHP异常Parse error: syntax error, unexpected T_VAR错误解决方法
May 06 PHP
Php连接及读取和写入mysql数据库的常用代码
Aug 11 PHP
PHP 实现的将图片转换为TXT
Oct 21 PHP
利用PHP命令行模式采集股票趋势信息
Aug 09 PHP
php使用glob函数遍历文件和目录详解
Sep 23 PHP
php变量与数组相互转换的方法(extract与compact)
Dec 02 PHP
php图形jpgraph操作实例分析
Feb 22 PHP
php注册审核重点解析(数据访问)
May 23 PHP
PHP接入支付宝接口失效流程详解
Nov 10 PHP
thinkPHP批量删除的实现方法分析
Nov 09 #PHP
基于jQueryUI和Corethink实现百度的搜索提示功能
Nov 09 #PHP
php 计算两个时间相差的天数、小时数、分钟数、秒数详解及实例代码
Nov 09 #PHP
php mysql procedure实现获取多个结果集的方法【基于thinkPHP】
Nov 09 #PHP
php利用gd库为图片添加水印
Nov 09 #PHP
php 输出json及显示json中的中文汉字详解及实例
Nov 09 #PHP
Yii2框架制作RESTful风格的API快速入门教程
Nov 08 #PHP
You might like
PHP 解决utf-8和gb2312编码转换问题
2010/03/18 PHP
PHP字符串处理的10个简单方法
2010/06/30 PHP
php 伪静态之IIS篇
2014/06/02 PHP
Yii调试SQL的常用方法
2014/07/09 PHP
php开发工具有哪五款
2015/11/09 PHP
ThinkPHP5.0框架控制器继承基类和自定义类示例
2018/05/25 PHP
PHP函数积累总结
2019/03/19 PHP
尽可能写&quot;友好&quot;的&quot;Javascript&quot;代码
2007/01/09 Javascript
一个用javascript写的select支持上下键、首字母筛选以及回车取值的功能
2009/09/09 Javascript
jquery取消选择select下拉框示例代码
2014/02/22 Javascript
Javascript的表单验证-提交表单
2016/03/18 Javascript
jQuery实现的倒计时效果实例小结
2016/04/16 Javascript
Angular 4依赖注入学习教程之FactoryProvider配置依赖对象(五)
2017/06/04 Javascript
Vue.js实现价格计算器功能
2020/03/30 Javascript
浅谈ajax请求不同页面的微信JSSDK问题
2018/02/26 Javascript
vuex中的 mapState,mapGetters,mapActions,mapMutations 的使用
2018/04/13 Javascript
浅谈VUE单页应用首屏加载速度优化方案
2018/08/28 Javascript
React通过redux-persist持久化数据存储的方法示例
2019/02/14 Javascript
Vue.js 中制作自定义选择组件的代码附演示demo
2020/02/28 Javascript
基于element-ui封装表单金额输入框的方法示例
2021/01/06 Javascript
Python的Flask开发框架简单上手笔记
2015/11/16 Python
Python 模板引擎的注入问题分析
2017/01/01 Python
从头学Python之编写可执行的.py文件
2017/11/28 Python
PyQt5基本控件使用详解:单选按钮、复选框、下拉框
2019/08/05 Python
iPython pylab模式启动方式
2020/04/24 Python
canvas实现图片镜像翻转的2种方式
2020/07/22 HTML / CSS
Nisbets法国:英国最大的厨房和餐饮设备供应商
2019/03/18 全球购物
在数据文件自动增长时,自动增长是否会阻塞对文件的更新
2014/05/01 面试题
大学生旷课检讨书
2014/01/22 职场文书
服务员自我评价
2014/01/25 职场文书
不假外出检讨书
2014/01/27 职场文书
法定代表人授权委托书
2014/04/04 职场文书
商业计划书范文
2019/04/24 职场文书
为什么中国式养孩子很累?
2019/08/07 职场文书
MySQL中一条update语句是如何执行的
2022/03/16 MySQL
我们认为中短波广播场强仪的最佳组合
2022/04/05 无线电