数据表结构和数据
表结构
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; } }
暂无评论...