快精灵印艺坊 您身边的文印专家
广州名片 深圳名片 会员卡 贵宾卡 印刷 设计教程
产品展示 在线订购 会员中心 产品模板 设计指南 在线编辑
 首页 名片设计   CorelDRAW   Illustrator   AuotoCAD   Painter   其他软件   Photoshop   Fireworks   Flash  

 » 彩色名片
 » PVC卡
 » 彩色磁性卡
 » 彩页/画册
 » 个性印务
 » 彩色不干胶
 » 明信片
   » 明信片
   » 彩色书签
   » 门挂
 » 其他产品与服务
   » 创业锦囊
   » 办公用品
     » 信封、信纸
     » 便签纸、斜面纸砖
     » 无碳复印纸
   » 海报
   » 大篇幅印刷
     » KT板
     » 海报
     » 横幅

无限分类与树型论坛的实现方式

无限分类与树型论坛的实现方式
?D?D浮点型字段排序法
Joe Teng 2005.6.12

在此我不想讨论其他实现方式的利与弊。
既然是使用字段排序,那么我们便设一个名为order的字段。问题是,在这里是使用整数还是使用浮点数类型呢?考虑到会有在两个连续order值中间插入新值的可能,自然是需要使用浮点类型了。
建一个menus表,我们还需要以下字段:
id : 类别编号
mainid : 主分类编号,但不作详细分类使用。假如在树型论坛里,它代表的是主题id
parentid : 父类编号
level : 类别级别,作用其实是方便显示的时候作其他处理
info : 类别名称等。
由此可以得到menus的表结构:
CREATE TABLE `menus` (
`id` INT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
`mainid` INT( 10 ) UNSIGNED NOT NULL ,
`parentid` INT( 10 ) UNSIGNED NOT NULL ,
`order` FLOAT UNSIGNED NOT NULL ,
`level` SMALLINT( 5 ) UNSIGNED NOT NULL ,
`info` VARCHAR( 128 ) NOT NULL ,
INDEX ( `mainid` , `parentid` , `order` , `level` ) ,
UNIQUE (
`id`
)
) TYPE = MYISAM ;

很轻易可以看出,输入的时候是如此简朴便实现树结构了:
SELECT * FROM `menus` ORDER BY `mainid` ASC, `order` ASC ;
前提是添加类别的时候,order能准确排序。

添加根分类:
很简朴,取得上一个主类的mainid, 如A_mainid,则新根分类的mainid则为A_mainid + 1。parentid 为 0 , order 为0, level也为0, info则自行设定。

添加子分类:
核心思想是,取得新增子分类的前一个分类的order以及它后一个分类的order。
取得前一个分类的order是这里的难点,因为涉及到同级与非同级的情况。非同级的情况很简朴,新增别类的前一个order其实就是它的父类的order。假如有同级分类,情况就很复杂了,因为它前面的同级分类有可能会拥有子分类,子分类下又可能还会有子分类,如此下来,要取得前一个order就很难了。
解决的办法有两个:
1.取得新增类同级的前一个类别,如类别A的ID,使用递归的方式,直到取得A类别下最后最小分类的order,那便是要新增分类的前一个order了。这种方式的缺点是,假如A类别下有很多子分类,那么递归需要一定的时间。这种方式适用于普通的分类处理,不适用于树型论坛。不过总体来说,因为是添加类别的时候才使用递归,输出类别的时候跟前面相同,效率还是很高的。
2.作一个记录,记录着与A有关联的最后order。于是我们就需要增加一个表,建利关系树。这种关系树做起来很简朴。表结构如下:
CREATE TABLE `menu_tree` (
`mainid` int(10) unsigned NOT NULL default \\'0\\',
`tree` text NOT NULL,
`order` float unsigned NOT NULL default \\'0\\',
KEY `mainid` (`mainid`,`order`),
FULLTEXT KEY `tree` (`tree`)
) TYPE=MyISAM;
(构建方法请看我后面给出的源码)
取得前一个order之后,要取得后一个order就很简朴了。取同mainid下大于前一个order的最小order便是了。假如存在后一个order,那么新增order就取前一个order与后一个order的平均值。假如不存在后一个order,那说明新增类别是main下的最小order,取大于前一个order的最小整数就行了。

主要实现方式便如上面说的。

处理方式

<?php
$arrDatabase = array
(
"host" => \\'localhost\\', "user" => \\'root\\', "password" => \\'123456\\', "dbname" => \\'test\\'
);

$resDbc = mysql_connect ( $arrDatabase["host"], $arrDatabase["user"], $arrDatabase["password"] );
mysql_select_db( $arrDatabase[\\'dbname\\'] );

if ( ! class_exists ( "FreeRoad" ))
{
class FreeRoad
{
var $resDbc ;
var $strDatabase ;
var $strMenuTable ;
var $strMenuTreeTable ;

var $strFiled_id = \\'id\\' ;
var $strFiled_mainid = \\'mainid\\' ;
var $strFiled_parentid = \\'parentid\\' ;
var $strFiled_order = \\'order\\' ;
var $strFiled_level = \\'level\\' ;

function FreeRoad ( $resDbc , $strDatabase , $strMenuTable , $strMenuTreeTable , $arrSetFileds = array() )
{
$this->resDbc = $resDbc ;
$this->strDatabase = $strDatabase;
$this->strMenuTable = $strMenuTable ;
$this->strMenuTreeTable = $strMenuTreeTable ;

if ( sizeof ( $arrSetFileds ) > 0 )
{
$this->strFiled_id = $arrSetFileds[\\'id\\'] ;
$this->strFiled_mainid = $arrSetFileds[\\'mainid\\'] ;
$this->strFiled_parentid = $arrSetFileds[\\'parentid\\'] ;
$this->strFiled_order = $arrSetFileds[\\'order\\'] ;
$this->strFiled_level = $arrSetFileds[\\'level\\'] ;
}
}

function get_new_mainid ()
{
mysql_select_db ( $this->strDatabase , $this->resDbc ) ;

$strQuery = " SELECT `$this->strFiled_mainid`
FROM `$this->strMenuTable`
WHERE `$this->strFiled_parentid` = 0
ORDER BY `$this->strFiled_id` DESC LIMIT 0 , 1 " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_array ( $resResult ) )
{
$intLastedMainId = $arrRow[0] ;
}
$intLastedMainId = intval ( $intLastedMainId );
mysql_free_result ( $resResult ) ;
return $intLastedMainId + 1 ;
}

function get_level_lastest_id ( $intParentId )
{
mysql_select_db ( $this->strDatabase , $this->resDbc ) ;
$strQuery = " SELECT `$this->strFiled_id`
FROM `$this->strMenuTable`
WHERE `$this->strFiled_parentid` = $intParentId
ORDER BY `$this->strFiled_id` DESC LIMIT 0 , 1 " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$intLevelLastestId = $arrRow[0] ;
}
mysql_free_result ( $resResult ) ;
return $intLevelLastestId ;
}

function get_level_lastest_order ( $intParentId )
{
mysql_select_db ( $this->strDatabase , $this->resDbc ) ;
$strQuery = " SELECT `$this->strFiled_order`
FROM `$this->strMenuTable`
WHERE `$this->strFiled_id` = $intParentId " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$floSelectItemOrder = $arrRow[0] ;
}
mysql_free_result ( $resResult ) ;

$strQuery = " SELECT `$this->strFiled_order`
FROM `$this->strMenuTreeTable`
WHERE BINARY ( `tree`) LIKE \\'%|$intParentId|%\\'
ORDER BY `$this->strFiled_order` DESC LIMIT 0 , 1 " ;
//echo $strQuery ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$floSelectItemLastestOrder = $arrRow[0] ;
}
mysql_free_result ( $resResult ) ;
if ( ! $floSelectItemLastestOrder ) $floSelectItemLastestOrder = $floSelectItemOrder ;
return $floSelectItemLastestOrder ;
}

function get_elements ( $intParentId = 0 )
{
mysql_select_db ( $this->strDatabase , $this->resDbc ) ;
if ( $intParentId == 0 )
{
$intMainId = $this->get_new_mainid ( );
return array ( "mainid" => $intMainId , "order" => 0 , "level" => 0 ) ;
}

$strQuery = " SELECT `$this->strFiled_mainid` , `$this->strFiled_order` , `$this->strFiled_level`
FROM `$this->strMenuTable`
WHERE `$this->strFiled_id` = $intParentId " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$intMainId = $arrRow[0] ;
$floOrder = $arrRow[1] ;
$intLevel = $arrRow[2] ;
}
mysql_free_result ( $resResult ) ;

if ( ! $intMainId ) return false ;

$intLevelLastestId = $this->get_level_lastest_id ( $intParentId ) ;

// get pre order
if ( $intLevelLastestId )
{
$floPreOrder = $this->get_level_lastest_order ( $intLevelLastestId );
// echo $floPreOrder ;exit;
}
else
{
$floPreOrder = $floOrder ;
}

// get next order
$strQuery = " SELECT `$this->strFiled_order`
FROM `$this->strMenuTable`
WHERE `$this->strFiled_mainid` = $intMainId AND `$this->strFiled_order` > $floPreOrder
ORDER BY `$this->strFiled_order` ASC LIMIT 0 , 1 " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$floNextOrder = $arrRow[0] ;
}
mysql_free_result ( $resResult ) ;

if ( ! $floNextOrder )
{
$floNewOrder = floor ( $floPreOrder + 1 ) ;
}
else
{

$floNewOrder = number_format ( ( $floPreOrder + $floNextOrder ) / 2 , 14 ) ;
}

$intNewLevel = $intLevel + 1 ;
return array ( "mainid" => $intMainId , "order" => $floNewOrder , "level" => $intNewLevel ) ;
}

function update_tree ( $intMainId , $intParentId , $floOrder )
{
if ( !$intParentId ) return false ;

mysql_select_db ( $this->strDatabase , $this->resDbc ) ;

$strQuery = " SELECT `tree`
FROM `$this->strMenuTreeTable`
WHERE `mainid` = $intMainId AND BINARY ( `tree`) LIKE \\'%|$intParentId|\\'
ORDER BY `order` DESC LIMIT 0 , 1 " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$strTree = $arrRow[0] ;
}
mysql_free_result ( $resResult ) ;

if ( ! $strTree )
{
$strQuery = " SELECT `$this->strFiled_parentid`
FROM `$this->strMenuTable`
WHERE `$this->strFiled_id` = $intParentId " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$intPreParentId = $arrRow[0] ;
}
mysql_free_result ( $resResult ) ;

if ( ! $intPreParentId )
{
$strPreTree = \\'\\';
}
else
{
$strQuery = " SELECT `tree`
FROM `$this->strMenuTreeTable`
WHERE `mainid` = $intMainId AND BINARY ( `tree`) LIKE \\'%|$intPreParentId|\\'
ORDER BY `order` DESC LIMIT 0 , 1 " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$strPreTree = $arrRow[0] ;
}
mysql_free_result ( $resResult ) ;
}

$strNewTree = $strPreTree . \\'|\\'. $intParentId . \\'|\\' ;
$strQuery = " INSERT INTO `$this->strMenuTreeTable`
VALUES ( $intMainId, \\'$strNewTree\\', $floOrder ) " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
@mysql_free_result ( $resResult ) ;
return true ;
}
else
{
$strQuery = " UPDATE `$this->strMenuTreeTable`
SET `order` = $floOrder
WHERE `mainid` = $intMainId AND `tree` = \\'$strTree\\' " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
@mysql_free_result ( $resResult ) ;
return true ;
}
}
}
}

/*
$pFreeRoad = new FreeRoad ( $resDbc , $arrDatabase["dbname"] , \\'menus\\' , \\'menu_tree\\' ) ;

$info = \\'change here\\';

$intParentId = change here ;
$arrItems = $pFreeRoad->get_elements( $intParentId ) ;
$intMainId = $arrItems[\\'mainid\\'] ;
$floOrder = $arrItems[\\'order\\'] ;
$intLevel = $arrItems[\\'level\\'] ;

$strQuery = " INSERT INTO `menus` VALUES ( \\'\\' , $intMainId , $intParentId , $floOrder , $intLevel, \\'$info\\' ) " ;
$resResult = mysql_query ( &$strQuery , $resDbc ) ;

$pFreeRoad->update_tree ( $intMainId , $intParentId , $floOrder ) ;

@mysql_close( $resDbc ) ;
*/
?>

<?php
include "freeroad.class.php";
$strQuery = " SELECT * FROM `menus` ORDER BY `mainid` ASC , `order` ASC ";
$resResult = mysql_query ( &$strQuery , $resDbc ) ;
while ( $arrRows = mysql_fetch_array ( $resResult ))
{
$intLevel = $arrRows["level"] ;
$strSpace = \\'\\' ;
for ( $i = 0 ; $i <= $intLevel ; $i++ )
{
$strSpace .= "  ";
}
if ( $i>1 )
{
$strSpace .= \\'--\\';
}
echo $strSpace . $arrRows["id"] . $arrRows["info"] ."<br>";
}

if ( $_GET["action"] == \\'add\\' )
{

$pFreeRoad = new FreeRoad ( $resDbc , $arrDatabase["dbname"] , \\'menus\\' , \\'menu_tree\\' ) ;

$info = \\'F1\\';

$intParentId = 1 ;
$arrItems = $pFreeRoad->get_elements( $intParentId ) ;
$intMainId = $arrItems[\\'mainid\\'] ;
$floOrder = $arrItems[\\'order\\'] ;
$intLevel = $arrItems[\\'level\\'] ;

$strQuery = " INSERT INTO `menus` VALUES ( \\'\\' , $intMainId , $intParentId , $floOrder , $intLevel, \\'$info\\' ) " ;
$resResult = mysql_query ( &$strQuery , $resDbc ) ;

$pFreeRoad->update_tree ( $intMainId , $intParentId , $floOrder ) ;

@mysql_close( $resDbc ) ;

}
?>
返回类别: 教程
上一教程: 自动生成静态页面
下一教程: 生成BMP格式的图片(IMAGEBMP)

您可以阅读与"无限分类与树型论坛的实现方式"相关的教程:
· 无限分类&树型论坛的实现
· php+mysql实现无限级分类 | 树型显示分类关系
· 一棵php的类树(支持无限分类)
· PHP文本型数据库分类排序的实现
· 一个无限分类的处理类
    微笑服务 优质保证 索取样品