【Mysql左右值】左右值法实现Mysql无限级分类-代码例子

  • 内容
  • 相关

数据表结构和数据

表结构

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】

index.php 主入口

<?php  
/** 
 * action 
 * showAll      查看整个分类树 
 * showOne      查看某个分类的子类 
 * showPath     查看某个子类到根分类的路径 
 * showAdd      显示增加分类的界面 
 * add          增加子分类 
 * delete       删除分类 
 * showModify   显示修改分类信息的界面 
 * modify       修改分类信息 
 * 
 * 假设已经有个  根分类  郑州 
 */  
(isset($_GET['action']) && $action = $_GET['action'] ) || $action = 'showAll';  
header('Content-type:text/html;charset=utf-8');  
  
//实例化分类操作类  
include_once './db/DB.class.php';  
$conf = include_once './conf.php';  
include_once './lib/ClassifyTree.class.php';  
$tree = new ClassifyTree($conf);  
  
/** 
 * [显示增加分类的界面] 
 * @return [boolean] [插入成功,返回true;否则,返回false] 
 */  
function add(){  
    global $tree;  
    $parentId = $_GET['parent'];  
    $name     = $_GET['name'];  
    $result   = $tree->insertNew($parentId, $name);  
    return $result;  
}  
  
/** 
 * [显示增加分类页面] 
 */  
function showAdd(){  
    $parentId = $_GET['parent'];  
    echo '<form action="." method="get">';  
    echo '<input type="hidden" name="action" value="add">';  
    echo '<br/>名称:<input type="text" name="name" style="width:200px;">';  
    echo '<input type="hidden" name="parent" value="'.$parentId.'">';  
    echo '<br/><input type="submit" style="width:130px;height:25px;"></form>';  
}  
  
/** 
 * [显示一个子分类下的所有分类] 
 */  
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 '<form action="." method="get">';  
    echo '<input type="hidden" name="action" value="modify">';  
    echo '名称:<input type="text" name="name" value="'.$info['name'].'" style="width:200px;height:25px;">';  
    echo '<input type="hidden" name="id" value="'.$info['id'].'">';  
    echo '<br/><input type="submit" style="width:120px;height:25px;">';  
    echo '</form>';  
}  
  
/** 
 * [修改分类信息] 
 * @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 '<a href="?action=showPath&id='.$oneStep['id'].'">'.$oneStep['name'].'</a> >> ';  
    }  
}  
  
/** 
 * [递归显示分类信息] 
 * @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(<br/>';  
            displayClassify($val, $interval+1);  
            indentation($interval);  
            echo ')<br/>';  
        }else{  
            echo ' => '.$val;  
            if('id' == $key){  
                echo ' <a href="?action=showAdd&parent='.$val.'">增加子节点</a>';  
                echo ' <a href="?action=showModify&id='.$val.'">修改节点</a>';  
                echo ' <a href="?action=delete&id='.$val.'">删除节点</a>';  
                echo '<br/>';  
                indentation($interval);  
                getPath($val);  
            }  
            echo '<br/>';  
        }  
    }  
}  
  
function indentation($interval){  
    for($i=0;$i<$interval;$i++){  
        echo '    ';  
    }  
}  
//执行请求  
$result = $action();  
  
if(false === $result){  
    echo 'failed<br/>';  
    echo $tree->getError();  
}else{  
    echo 'success';  
}

conf.php 配置文件

<?php  
return array(  
        'dbHost'=>'localhost',  
        'dbName'=>'classify_infinite',  
        'dbUser'=>'root',  
        'dbPass'=>'root',  
        'dbCharset'=>'utf8'  
    );

DB.class.php 数据库操作类

<?php  
/** 
 * 数据库操作类 
 * 执行数据库的增删改查操作 
 */  
class DB {  
    private $dbHost;//数据库主机名  
    private $dbName;//数据库名称  
    private $username;//数据库用户  
    private $password;//数据库密码  
    private $charset;//数据库字符集编码  
    private $link;//数据库连接资源  
    private $error = null;//错误信息  
    /** 
     * 构造函数 
     * 根据传递进来的conf数组给局部变量赋初值 
     * 连接数据库 
     * @param array $conf 
     */  
    public function __construct($conf) {  
        if(!isset($conf['dbHost']) || !isset($conf['dbName']) || !isset($conf['dbUser']) || !isset($conf['dbPass']) || !isset($conf['dbCharset'])){  
            $this->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');  
    }  
}


ClassifyTree.class.php 文件

<?php  
class ClassifyTree  
{  
    private $db;  
    private $error;  
    public function __construct($conf){  
        $this->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无限级分类-代码例子 - http://tv1314.com/post-329.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注

00:00 / 00:00
顺序播放