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 相关文章推荐
介绍php设计模式中的工厂模式
Jun 12 PHP
php iconv() : Detected an illegal character in input string
Dec 05 PHP
DISCUZ在win2003环境下 Unable to access ./include/common.inc.php in... 的问题终极解决方案
Nov 21 PHP
PHP输出数组中重名的元素的几种处理方法
Sep 05 PHP
PHP多线程批量采集下载美女图片的实现代码(续)
Jun 03 PHP
有关于PHP中常见数据类型的汇总分享
Jan 06 PHP
Windows中使用计划任务自动执行PHP程序实例
May 09 PHP
ThinkPHP惯例配置文件详解
Jul 14 PHP
PHP解码unicode编码的中文字符代码分享
Aug 13 PHP
浅析php适配器模式(Adapter)
Nov 25 PHP
php获取QQ头像并显示的方法
Dec 23 PHP
php求今天、昨天、明天时间戳的简单实现方法
Jul 28 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
基于mysql的论坛(5)
2006/10/09 PHP
PHP初学者常见问题集合 修正版(21问答)
2010/03/23 PHP
深入理解PHP JSON数组与对象
2016/07/19 PHP
php获取目录下所有文件及目录(多种方法)(推荐)
2019/05/14 PHP
syntaxhighlighter 使用方法
2007/07/02 Javascript
JavaScript 利用StringBuffer类提升+=拼接字符串效率
2009/11/24 Javascript
JavaScript 原型继承
2011/12/26 Javascript
javascript实现类似java中getClass()得到对象类名的方法
2015/07/27 Javascript
jQuery移动web开发中的页面初始化与加载事件
2015/12/03 Javascript
js实现div模拟模态对话框展现URL内容
2016/05/27 Javascript
JS实现兼容各种浏览器的获取选择文本的方法【测试可用】
2016/06/21 Javascript
JavaScript制作弹出层效果
2016/12/02 Javascript
jQuery判断自定义属性data-val用法示例
2019/01/07 jQuery
微信小程序蓝牙连接小票打印机实例代码详解
2019/06/03 Javascript
vue+webpack 更换主题N种方案优劣分析
2019/10/28 Javascript
javaScript代码飘红报错看不懂?读完这篇文章再试试
2020/08/19 Javascript
[01:08:57]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS LGD第二场
2014/05/24 DOTA
python去除拼音声调字母,替换为字母的方法
2018/11/28 Python
python批量下载网站马拉松照片的完整步骤
2018/12/05 Python
python爬虫URL重试机制的实现方法(python2.7以及python3.5)
2018/12/18 Python
PyQtGraph在pyqt中的应用及安装过程
2019/08/04 Python
Python一键查找iOS项目中未使用的图片、音频、视频资源
2019/08/12 Python
使用Matplotlib 绘制精美的数学图形例子
2019/12/13 Python
Pytorch 数据加载与数据预处理方式
2019/12/31 Python
TensorFlow-gpu和opencv安装详细教程
2020/06/30 Python
Python列表嵌套常见坑点及解决方案
2020/09/30 Python
CSS3实现全景图特效示例代码
2018/03/26 HTML / CSS
Lime Crime官网:美国一家主打梦幻精灵系的彩妆品牌
2019/03/22 全球购物
俄罗斯极限运动网上商店:Board Shop №1
2020/12/18 全球购物
前厅收银主管岗位职责
2014/02/04 职场文书
开业主持词
2014/03/21 职场文书
产品陈列协议书(标准版)
2014/09/17 职场文书
农民工工资支付承诺书
2015/05/04 职场文书
2015年秋季开学典礼校长致辞
2015/07/16 职场文书
一波干货,会议主持词开场白范文
2019/05/06 职场文书
MySQL中dd::columns表结构转table过程及应用详解
2022/09/23 MySQL