CREATE TABLE `classify` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) NOT NULL COLLATE 'utf8_general_ci',
`lft` INT(11) NOT NULL,
`rgt` INT(11) NOT NULL,
`parentId` INT(11) NOT NULL,
PRIMARY KEY (`id`),
INDEX `scope` (`lft`, `rgt`)
)
COLLATE='utf8_bin'
ENGINE=InnoDB
AUTO_INCREMENT=13;
INSERT INTO `classify` VALUES (1, '郑州', 1, 16, -1);
INSERT INTO `classify` VALUES (19, '荥阳', 10, 15, 1);
INSERT INTO `classify` VALUES (20, '荥阳东', 13, 14, 19);
INSERT INTO `classify` VALUES (21, '荥阳西', 11, 12, 19);
INSERT INTO `classify` VALUES (22, '开封', 2, 9, 1);
INSERT INTO `classify` VALUES (23, '开封东', 7, 8, 22);
INSERT INTO `classify` VALUES (24, '开封西', 5, 6, 22);
INSERT INTO `classify` VALUES (25, '开封府', 3, 4, 22);
/——
|--index.php 入口文件
|--conf.php 配置文件
/db
|--DB.class.php 数据库操作类
/lib
|--ClassifyTree.class.php 左右树操作文件【core】
insertNew($parentId, $name);
return $result;
}
/**
* [显示增加分类页面]
*/
function showAdd(){
$parentId = $_GET['parent'];
echo '';
}
/**
* [显示一个子分类下的所有分类]
*/
function showOne(){
global $tree;
$id = $_GET['id'];
$classifyInfo = $tree->getOne($id);
//递归显示分类信息
displayClassify($classifyInfo);
}
/**
* [显示某个分类到根分类的路径]
*/
function showPath(){
global $tree;
$id = $_GET['id'];
$pathArr = $tree->getPath($id);
displayPath($pathArr);
}
function getPath($id){
global $tree;
$pathArr = $tree->getPath($id);
displayPath($pathArr);
}
/**
* [显示所有的分类信息]
* @return [type] [description]
*/
function showAll(){
global $tree;
$classifyArr = $tree->getAll();
displayClassify($classifyArr);
}
/**
* [删除一个分类]
* @return [boolean] [删除成功,则返回true;否则,则返回false]
*/
function delete(){
global $tree;
$id = $_GET['id'];
$result = $tree->delete($id);
return $result;
}
/**
* [显示修改id的界面]
* @return [bolean] [如果查询失败,则返回false]
*/
function showModify(){
global $tree;
$id = $_GET['id'];
$info = $tree->searchById($id);
if(false === $info){
return false;
}
echo '';
}
/**
* [修改分类信息]
* @return [boolean] [修改成功,则返回true;否则,返回false]
*/
function modify(){
global $tree;
$name = $_GET['name'];
$id = $_GET['id'];
$result = $tree->modify($name, $id);
return $result;
}
/**
* [输出路径数组]
* @param [array] $path [路径数组]
*/
function displayPath($path){
foreach($path as $oneStep){
echo ''.$oneStep['name'].' >> ';
}
}
/**
* [递归显示分类信息]
* @param [array] $classify [分类信息]
* @param [int] $interval [缩进长度]
*/
function displayClassify($classify, $interval=0){
foreach($classify as $key=>$val){
for($i=0;$i<$interval;$i++){
echo ' ';
}
echo $key;
if(is_array($val)){
echo ' => array(
';
displayClassify($val, $interval+1);
indentation($interval);
echo ')
';
}else{
echo ' => '.$val;
if('id' == $key){
echo ' 增加子节点';
echo ' 修改节点';
echo ' 删除节点';
echo '
';
indentation($interval);
getPath($val);
}
echo '
';
}
}
}
function indentation($interval){
for($i=0;$i<$interval;$i++){
echo ' ';
}
}
//执行请求
$result = $action();
if(false === $result){
echo 'failed
';
echo $tree->getError();
}else{
echo 'success';
}
'localhost',
'dbName'=>'classify_infinite',
'dbUser'=>'root',
'dbPass'=>'root',
'dbCharset'=>'utf8'
);
error = '配置不完整';
}else{
$this->dbHost = $conf['dbHost'];
$this->dbName = $conf['dbName'];
$this->username = $conf['dbUser'];
$this->password = $conf['dbPass'];
$this->charset = $conf['dbCharset'];
//连接数据库服务器
$this->link = mysql_connect($this->dbHost,$this->username,$this->password);
mysql_query("SET NAMES '{$this->charset}'");
if(false === $this->link){
$this->error = '连接数据库失败';
}elseif(!mysql_select_db($this->dbName,$this->link)){//选择数据库
$this->error = '连接数据库失败';
}
}
}
/**
* 执行查询操作
* @param string $sql 要执行的sql
* @param [boolean] $key [如果标记为true,将结果以id为主键返回;如果标记为false,将结果以数字为主键返回]
* @return boolean|array 如果执行出错,则记录错误,返回false;否则,返回查询得到的结果集
*/
public function queryList($sql, $key=false){
$resource = mysql_query($sql);
if(false === $resource){
$this->error = mysql_error();
return false;
}
$result = array();
while ($row = mysql_fetch_assoc($resource)) {
if(false === $key){
array_push($result,$row);
}else{
$result[$row['id']] = $row;
}
}
return $result;
}
/**
* [执行查询操作,返回一个结果数组]
* @param [string] $sql [要执行的sql]
* @return [array|false] [查询得到的结果集;如果查询出错,返回false]
*/
public function queryOne($sql){
$resource = mysql_query($sql);
if(false === $resource){
$this->error = mysql_error();
return false;
}
$result = mysql_fetch_assoc($resource);
return $result;
}
/**
* 执行插入操作
* @param string $sql 要执行的sql
* @return boolean 如果执行出错,则记录错误并返回false;如果执行成功,则直接返回插入的数据的id
*/
public function insert($sql){
$result = mysql_query($sql);
if(false === $result){
$this->error = mysql_error();
return false;
}else{
return mysql_insert_id();
}
}
/**
* 执行更新操作
* @param string $sql 要执行的sql
* @return boolean 如果执行出错,则记录错误信息并返回false;如果执行成功,则直接返回所影响的行数
*/
public function update($sql){
$result = mysql_query($sql);
if(false === $result){
$this->error = mysql_error();
return false;
}else{
return mysql_affected_rows();
}
}
/**
* 执行删除操作
* 调用更新操作来完成
*/
public function delete($sql){
return $this->update($sql);
}
/**
* 获取错误信息
* 如果错误信息不存在,则返回null
* @return string 返回值;返回当前的错误信息
*/
public function getError(){
return $this->error;
}
public function __destruct(){
mysql_close($this->link);
}
/**
* 开启事务
*/
public function begin()
{
mysql_query('begin');
}
/**
* 事务回滚
*/
public function rollBack()
{
mysql_query('rollback');
}
/**
* 事务提交
*/
public function submit()
{
mysql_query('commit');
}
}
db = new DB($conf);
$this->error = $this->db->getError();
}
/**
* [返回错误信息]
* @return [string] [错误信息]
*/
public function getError(){
return $this->error;
}
/**
* [获取整个分类树结构]
* @return [array|boolean] [如果查询失败,则返回false;否则,返回格式化后的分类数组]
*/
public function getAll(){
$sql = 'SELECT `id`,`name`,`lft`,`rgt`,`parentId` FROM `classify`';
$classifyInfo = $this->db->queryList($sql);
if(false === $classifyInfo){
$this->error = '查询出错。'.$this->db->getError();
return false;
}
//格式化数组
$result = $this->format($classifyInfo);
return $result;
}
/**
* [格式化检索到的分类数据库中的数据,将信息组织成
* array(
* array(** 分类a **)
* 'childrens'=>array(
* array(** 分类b **)
* 'childrens'=>array(
* ......
* )
* )
* )
* 的形式
* ]
* @param [array] $classifyInfo [需要格式化的数据]
* @param [integer] $parentId [父分类的id]
* @return [array] [格式化后的数据]
*/
private function format(&$classifyInfo, $parentId=-1){
$result = array();//需要返回的结果数组
foreach($classifyInfo as $key=>$oneInfo){
if($parentId == $oneInfo['parentId']){
$childrens = $this->format($classifyInfo, $oneInfo['id']);
if(!empty($childrens)){
$oneInfo['childrens'] = $childrens;
}
$result[] = $oneInfo;
}
}
return $result;
}
/**
* [获取某个分类到根分类的路径]
* @param [int] $id [分类id]
* @return [array|boolean] [如果查询失败,则返回false;否则,返回路径数组]
*/
public function getPath($id){
//查询$id的分类和根分类的左右值
$sql = 'SELECT `id`,`lft`,`rgt` FROM `classify` WHERE `id`='.$id.' or `parentId`=-1';
$classifyInfo = $this->db->queryList($sql, true);
if(false === $classifyInfo){
$this->error = '查询失败:'.$this->db->getError();
return false;
}
if(1 != count($classifyInfo)){
$left = $classifyInfo[$id]['lft'];
$right = $classifyInfo[$id]['rgt'];
unset($classifyInfo[$id]);
$classifyInfo = array_pop($classifyInfo);
$rootLeft = $classifyInfo['lft'];
$rootRight = $classifyInfo['rgt'];
}else{
$rootLeft = $left = $classifyInfo[$id]['lft'];
$rootRight = $right = $classifyInfo[$id]['rgt'];
}
//查询当前节点到根节点的距离
$sql = 'SELECT `id`,`name`,`lft`,`rgt`,`parentId` '
.'FROM `classify` '
.'WHERE `lft`>='.$rootLeft.' AND `lft`<='.$left.' AND `rgt`<='.$rootRight.' AND `rgt`>='.$right
.' ORDER BY `lft` ';
$classifyPath = $this->db->queryList($sql);
if(false === $classifyPath){
$this->error = '查询失败:'.$this->db->getError();
return false;
}
return $classifyPath;
}
/**
* [获取指定分类下的分类]
* @param [int] $id [分类id]
* @return [array|boolean] [如果查询失败,则返回false;否则,返回格式化后的分类数组]
*/
public function getOne($id){
//查询$id分类的左右值
$sql = 'SELECT `lft`,`rgt` FROM `classify` WHERE `id`='.$id;
$oneInfo = $this->db->queryOne($sql);
if(false === $classifyInfo){
$this->error = '查询失败:'.$this->db->getError();
return false;
}
$left = $oneInfo['lft'];
$right = $oneInfo['rgt'];
//查询该分类下的所有分类
$sql = 'SELECT `id`,`name`,`lft`,`rgt`,`parentId` FROM `classify` WHERE `lft`>='.$left.' AND `rgt`<='.$right;
$classifyInfo = $this->db->queryList($sql);
if(false === $classifyPath){
$this->error = '查询失败:'.$this->db->getError();
return false;
}
//格式化数组
$result = $this->format($classifyInfo);
return $result;
}
/**
* [在一个分类下添加子分类]
* @param [int] $parentId [父分类的id]
* @param [string] $name [增加的分类的名称]
* @return [boolean] [增加成功,则返回true;否则,返回false]
*/
public function insertNew($parentId, $name){
$this->db->begin();
//查询当前分类的左右值
$sql = 'SELECT `lft` FROM `classify` WHERE `id`='.$parentId;
$oneInfo = $this->db->queryOne($sql);
if(false === $oneInfo){
$this->error = '查询失败:'.$this->db->getError();
$this->db->rollBack();
return false;
}
$left = $oneInfo['lft'];
//将所有左值大于当前分类左值的分类左右值加2
$sql = 'UPDATE `classify` SET `lft`=`lft`+2,`rgt`=`rgt`+2 WHERE `lft`>'.$left;
$result = $this->db->update($sql);
if(false === $result){
$this->error = '更新节点左右值失败.'.$this->db->getError();
$this->db->rollBack();
return false;
}
//更新右节点大于当前分类左值的分类节点
$sql = 'UPDATE `classify` SET `rgt`=`rgt`+2 WHERE `rgt`>'.$left.' AND `lft`<='.$left;
$result = $this->db->update($sql);
if(false === $result){
$this->error = '更新节点右值失败.'.$this->db->getError();
$this->db->rollBack();
return false;
}
//插入新的节点
$left += 1;
$right = $left+1;
$sql = "INSERT INTO `classify` VALUES(null, '{$name}', {$left}, {$right}, {$parentId})";
$result = $this->db->insert($sql);
if(false === $result){
$this->error = '插入新节点失败.'.$this->db->getError();
$this->db->rollBack();
return false;
}
$this->db->submit();
return true;
}
/**
* [删除一个分类信息]
* @param [int] $id [分类id]
* @return [boolean] [删除成功,则返回true;否则,返回true]
*/
public function delete($id){
$this->db->begin();
//查询当前分类的左右值
$sql = 'SELECT `lft`,`rgt` FROM `classify` WHERE `id`='.$id;
$oneInfo = $this->db->queryOne($sql);
if(false === $oneInfo){print_r(debug_backtrace());
$this->error = '查询失败:'.$this->db->getError();
$this->db->rollBack();
return false;
}
$left = $oneInfo['lft'];
$right = $oneInfo['rgt'];
$dValue = $right - $left + 1;
//删除当前分类及其子分类
$sql = 'DELETE FROM `classify` WHERE `lft`>='.$left.' AND `rgt`<='.$right;
$result = $this->db->delete($sql);
if(false === $result){
$this->error = '删除失败:'.$this->db->getError();
$this->db->rollBack();
return false;
}
//将所有左值大于当前分类左值的分类左右值加2
$sql = 'UPDATE `classify` SET `lft`=`lft`-'.$dValue.' , `rgt`=`rgt`-'.$dValue.' WHERE `lft`>'.$right;
$result = $this->db->update($sql);
if(false === $result){
$this->error = '更新节点左值失败.'.$this->db->getError();
$this->db->rollBack();
return false;
}
//更新右节点大于当前分类左值的分类节点
$sql = 'UPDATE `classify` SET `rgt`=`rgt`-'.$dValue.' WHERE `lft`<'.$left.' AND '.' `rgt`>'.$left;
$result = $this->db->update($sql);
if(false === $result){
$this->error = '更新节点右值失败.'.$this->db->getError();
$this->db->rollBack();
return false;
}
$this->db->submit();
return true;
}
/**
* [根据id查询一个分类的信息]
* @param [int] $id [分类id]
* @return [array|boolean] [查询失败,则返回false;否则,返回分类信息数组]
*/
public function searchById($id){
//查询当前分类的左右值
$sql = 'SELECT `id`,`name`,`lft`,`rgt`,`parentId` FROM `classify` WHERE `id`='.$id;
$oneInfo = $this->db->queryOne($sql);
if(false === $oneInfo){
$this->error = '查询失败:'.$this->db->getError();
return false;
}
return $oneInfo;
}
/**
* [保存修改过的分类信息]
* @param [string] $newName [分类的新名称]
* @param [int] $id [分类id]
* @return [boolean] [如果修改成功,则返回true;否则,返回false]
*/
public function modify($newName, $id){
$sql = "UPDATE `classify` SET `name`='{$newName}' WHERE `id`={$id}";
$result = $this->db->update($sql);
if(false === $result){
$this->error = '更新失败:'.$this->db->getError();
return false;
}
return true;
}
}
左右值无限级分类
mysql左右值无限分类原理及实现
mysql 无限级分类实现思路
预排序遍历树算法(非递归无限极分类算法)学习笔记