三个Infiniti分类的管理类程序代码,帖多少个PHP的最棒分类实现主张

id, ……,netcat2@21cn.com

1、做网站的一般都会遇到处理分类的问题, 偶来帖几个处理无限分类的例子
数据库的结构很简单:
id, fatcher_id, name, ……
这样的设计短小精悍,完全满足3NF..可以完全绝大多数要求,OK,让偶们看看这种数据库结构下的程序实现.

 代码如下

1.递归查询数据库
最要命的做法~也是最好实现的做法
  类别1
    类别1.1
      类别1.1.1
    类别1.2
  类别2
    类别2.1
  类别3
    类别3.1
    类别3.2
……
为了生成这样的目录结构,程序递归一次就查询一次数据库,在您任何涉及分类的地方(翻页,查询….),数据库都会悲壮地操作硬盘….阿门~so~跳一下吧…
2.查询一次数据库,递归数组生成以上目录结构
帖一个大致的思路
function SelectList(&$Data, $RootID = 0′)
{
     for($i = 0; $i < count($Data); $i++)
     {
           if($Data[$i][‘UID’] == $RootID)
           {
                 ……//处理,直接生成HTML或者保存入数组都OK啦
                 $this->SelectRecursion($Data, $Data[$i][‘ID’], $blank, $Match);
           }
     }
     return $this->Output;
}
这种强度的递归一般的网站系够用啦~不过碰到BT点的,有几K甚至上W的分类,递归一遍可能要百毫秒以上,在考虑并发的情况下….呵呵~我们再跳一下吧
3.查询一次数据库,非递归生成目录结构
这一步,程序的技巧来了~只对结果遍历一次就能生成上面那样的目录结构,想要把它格式化成网页的显示样式就很方便咯~下面的系别人写的,偶试过完全可行
function GetArray($RootID = 0)
{
     $Data      = array();

<?php
/* 名称: 对分类操作的业务逻辑封装
*
* 作者: 帅的像人渣 QQ: 1191391 E-mail: netcat2@21cn.com
*
* 完成日期: 2003-12-18 13:33
*
* 说明:
本类中引用的其它类(DB、Table、Item)均未提供,所以本类只能做个参考,不能直接应用
*
不是本人小气不提供其它类,实在是因为那些都是一两年前写的类,很烂。怕大家看后对大
* 造成误导. 在此发表这个类,只希望大家能从中学到一些程序设计的方法。
* 授人以鱼不如授人以渔~
*
* 特点:
*
采用递归调用的方法,对分类数据只需一次数据库查询可生成树状结构。
无限递归层次(视机器堆栈而定)
*
* 数据库定义:
* ID smallint unsigned primary #如果数据量很大可用int
* ParentID smallint unsigned index #如果数据量很大可用int,
请索引此字段
* #如果为根分类,则ParentID = 0
*
* RootID smallint unsigned index #如果数据量很大可用int,
请索引此字段
* #如果是根分类则RootID = 0, 否则RootID = 最上层的父分类ID
* CategoryName varchar(n) #此大小自定
* 如需有其它字段定义附在后面

     $Data      = $tblObj->MapResult($tblObj->Select());

* 注意事项:
* 不要试图直接调用本类,除非你有和我定义那另外那几个类相对应的接口,
否则不会成功
* 在合适的地方定义 DBTABLE_CATEGORY
这个常量,使其指向你的分类数据表名字
*
* 程序构架:
* ├─基础类 <!–
完成底层数据库操作、数据抽象、语言、模板、异常、杂项等)操作 –>
* │
* │
* └─业务逻辑层(此类所处层次) <!–
利用基础类中数据操作、数据抽象等类根据表现层传递的参数完成数据处理,并返回数据或操作结果
–>
* │
* │
* └───表现层(用户界面) <!–
利用业务逻辑层将取得的数据或操作数据的结果通过基础类中的界面等类进行显示
–>
*/

    $Output = Array();
    $i = 0;
    $len = Count($Data);
    if($RootID)
    {
        while($Data[$i][‘UID’] != $RootID && $i < $len)      $i++;
    }
    $UpID   = $RootID;     //上个节点指向的分类父ID
    for($cnt = Count($Data); $i < $cnt;)   //历遍整个分类数组
    {
        $j = 0;     //初始化此次分类下子分类数据计数
        if ($UpID == $RootID)   //在第一次循环时将所有一级分类保存到$Output这个数组中
        {
          while($Data[$i][‘UID’] == $UpID && $i < $len) //判断上一个节点是否为兄弟节点
          {
            $Output[$j] = $Data[$i];                   //保存该节点到Output这个数组中
            $tmp[$Data[$i][‘ID’]] = &$Output[$j]; //并且将该节点ID在Output中的位置保存起来.
            $i++;
            $j++;
          }
        }
        else
        {
          while($Data[$i][‘UID’] == $UpID && $i < $len)
          {
            if($tmp[$UpID])
            {
                $tmp[$UpID][‘Child’][$j] = $Data[$i];
                $tmp[$Data[$i][‘ID’]] = &$tmp[$UpID][‘Child’][$j];   //保存该节点ID在Output中的位置
            }
            $i++;
            $j++;
          }
        }
        $UpID = $Data[$i][‘UID’];
    }
    return $Output;
  }

define(‘DBTABLE_CATEGORY’, ‘xxx’);

程序看着好累人啊~这段代码的效率比上一段快了十几倍呢,强度系够大了…
不过….90%的网站用这样的代码都浪费啦~偶遇到过的网站分类一般都在四级以下,那么,还能再优化一下么?……..再….再跳一下吧..
4.从数据库入手~ 稍微改动一下数据库的结构,加一个layer的冗余字段,这个是db desing高手想出来的,偶把他帖出来而已
id         name       father_id       layer
1         总类别       0           000000
2         类别1           1           010000
3         类别1.1         2           010100
4         类别1.2         2           010200
5         类别2           1           020000
6         类别2.1         5           020100
7         类别3           1           030000
8         类别3.1         7           030100
9         类别3.2         7           030200
10         类别1.1.1         3           010101

class Category_Logic
{
var $KernelRef = NULL; //系统核心的引用
var $tblObj = NULL; //包含当前分类数据 Table 类的实例

现在按layer的大小来检索一下:SELECT * FROM Type_table_2 ORDER BY type_layer

var $_CurrentItem = NULL; //包含当前分类数据 TItem类的实例

列出记录集如下:

var $CategoryID = 0; //当前分类ID,如果没有当前分类此项为 0

id         name       father_id       layer
1         总类别       0           000000
2         类别1           1           010000
3         类别1.1         2           010100
10         类别1.1.1         3           010101
4         类别1.2         2           010200
5         类别2           1           020000
6         类别2.1         5           020100
7         类别3           1           030000
8         类别3.1         7           030100
9         类别3.2         7           030200

//—————————————————————————
//private array GetNodeData(array $Data, int $ParentNode)
//
根据一颗指定根的并且以兄弟双亲法表示的树和当前分类的ID,返回当前分类在整个分类表中所处的位置
//
// @param: $Data 2维数组 Array(
// Array(
// ‘ID’ => 分类ID,
// ‘ParentID’ => 父分类ID,
// ‘RootID’ => 根分类ID,
// ‘CategoryName’ => 分类名称,
// ),
// ……
// );
// 表示的一颗树
//
// @param: $ParentNode 父分类ID,
每一次由调用者给出,递归时由程序计算传递
//
// return value: 返回以兄弟双亲法表示的所有分类的树
// 注意: 确保当前分类已经设置,否则此函数无返回
//
//—————————————————————————
function GetNodeData($Data, $ParentNode)
{
$arr = Array();

看见了么~一次查询连目录结构都生成好咯~,这样程序就轻松很多了,只是多了维护layer字段的工作,这个例程中layer字段每一级能处理99个分类,如果有BT的应用,改大些就行了,呵呵,先分析你的需求吧

$ArrayCount = 0;

OK~OVER~闪人~

for($i = 0, $cnt = Count($Data); $i < $cnt; $i++)
{
if($Data[$i][‘ParentID’] == $ParentNode)
{
$arr[$ArrayCount] = $Data[$i];
$arr[$ArrayCount++][‘Child’] = $this->GetNodeData($Data,
$Data[$i][‘ID’]);
}
}

您可能感兴趣的文章:

  • 使用PHP数组实现无限分类,不使用数据库,不使用递归.
  • PHP无限分类的类
  • php 无限分类的树类代码
  • php递归实现无限分类生成下拉列表的函数
  • PHP无限分类代码,支持数组格式化、直接输出菜单两种方式
  • PHP 无限分类三种方式
    非函数的递归调用!
  • php无限分类且支持输出树状图的详细介绍
  • PHP无限分类(树形类)
  • php无限分类使用concat如何实现

return $arr;
}

//—————————————————————————
//private String _CurrentLevel(array $Data, int $Current, String
$ProcessFunc = ”)
//
根据一颗指定根的并且以兄弟双亲法表示的树和当前分类的ID,返回当前分类在整个分类表中所处的位置
//
// @param: $Data 兄弟双亲法表示的树, 由调用者传递
//
// @param: $Current
当前分类ID,第一次调用时由调用者给出,递归时由程序自行计算
//
// @param: $ProcessFunc 指定对分类数据的处理函数, 函数原型定义见
$this->PrintCurrentLevel 中的注释
//
// return value: 返回当前分类在分类树中的位置
// 注意: 确保当前分类已经设置,否则此函数无返回
//
//—————————————————————————
function _CurrentLevel($Data, $Current, $ProcessFunc = ”)
{
for($i = 0; $i < Count($Data); $i++)
{
if($Data[$i][‘ID’] == $Current)
{
if($Data[$i][‘ParentID’] != 0)
{
$str = $this->_CurrentLevel($Data, $Data[$i][‘ParentID’],
$ProcessFunc) . ‘ -> ‘;

if($ProcessFunc) $str .= $ProcessFunc($Data[$i]);
else $str .= $Data[$i][‘CategoryName’];
}
else
{
if($ProcessFunc) $str = $ProcessFunc($Data[$i]);
else $str = $Data[$i][‘CategoryName’];
}
break;
}
}

return $str;
}

//—————————————————————————
//public Category_Logic(Object &$Kernel, int $CategoryID = -1)
// 本类构造函数
//
// @param: $Kernel 此参数为当前系统核心类的一个引用, 核心类中包括
// 数据库类、输入输出类、系统配置类等
//
// @param: $CategoryID 当前分类ID。
// 当想调用
PrintCurrentLevel、GetRootID、GetParentID、GenerateTypeTreeList及
// 调用_CurrentItem成员的方法时请先设置此值.
//
//
调用GenerateTypeTreeList时设置此值,则没有ID为此的分类默认被选择,没设置则无默认
//
// return value: none
//
//—————————————————————————
function &Category_Logic(&$Kernel, $CategoryID = -1)
{
$this->KernelRef = &$Kernel;

$this->tblObj = new Table($Kernel->DBObj, DBTABLE_CATEGORY);

if($CategoryID != -1)
{
$this->SetCategoryID($CategoryID);
}
}

//—————————————————————————
//public void SetCategoryID(int $CategoryID)
// 设置当前分类ID
//
// return value: none
//
//—————————————————————————
function SetCategoryID($CategoryID)
{
if(!$CategoryID) return;

$Item = new TItem($this->KernelRef->DBObj, DBTABLE_CATEGORY,
‘*’, $CategoryID ,’ID’);

$this->_SelfData = &$Item;

$this->CategoryID = $CategoryID;
}

//—————————————————————————
//public int GetRootID()
// 返回当前分类的根分类ID
// 注意:只有设置的当前分类时此函数才有效
//
// return value: 返回当前分类的根分类ID
//
//—————————————————————————
function GetRootID()
{
return $this->_SelfData->Get(‘RootID’);
}

//—————————————————————————
//public int GetParentID()
// 返回当前分类的父分类ID
// 注意:只有设置的当前分类时此函数才有效
//
// return value: 返回当前分类的父分类ID
//
//—————————————————————————
function GetParentID()
{
if($this->CategoryID) return
$this->_SelfData->Get(‘ParentID’);
}

//—————————————————————————
//public String GenerateTypeTreeList(array $Data, String $ProcessFunc,
int $floor = 0)
// 返回整个分类的树状结构放在OptionList中的列表
//
// @param: $Data 此参数由 $this->DumpTypeDataToTree() 返回
//
// @param: $ProcessFunc 处理显示分类信息的回调函数, 函数原型请参照:
$this->PrintCurrentLevel()
//
// @param: $floor 本参数不能人为给出,是程序自动计算的中间值
//
// return value:
// 结构为一颗兄弟双亲表示法表示的树
// 设如分类数据如下:
// ├──1级分类
// │
// │
// │
// ├─2级分类
// │ │
// │ └─3级分类
// │
// └─2级分类
//
// 则返回值为 Array(
// 0 => Array(
// ‘ID’ => ”,
// ‘ParentID’ => ”,
// ‘RootID’ => ”,
// ‘CategoryName’ => ”,
// ‘Child’ => ….
// )
// …..
// )
//
//—————————————————————————
function DumpTypeDataToTree($RootID = 0, $Fields = ‘*’)
{
$this->tblObj->SetFields($Fields);
$this->tblObj->SetCondition(”);

$List = $this->tblObj->MapResult($this->tblObj->Select());

return $this->GetNodeData($List, $RootID);
}

//—————————————————————————
//public String GenerateTypeTreeList(array $Data, String $ProcessFunc =
”, int $floor = 0)
// 返回整个分类的树状结构放在OptionList中的列表
//
// @param: $Data 此参数由 $this->DumpTypeDataToTree() 返回
//
// @param: $ProcessFunc 处理显示分类信息的回调函数, 函数原型请参照:
$this->PrintCurrentLevel()
//
// @param: $floor 本参数不能人为给出,是程序自动计算的中间值
//
// return value: 返回一个<option>分类名称1</option> …
<option>分类名称n</option>
//
// ps: 调用时echo
“<select name=’xxxx’>” .
$_c->GenerateTypeTreeList($Data, ‘ProcessFunc’) .
“</select>”;
//
//—————————————————————————
function GenerateTypeTreeList($Data, $ProcessFunc, $floor = 0)
{
$Str = ”;
for($i = 0, $cnt = Count($Data); $i < $cnt; $i++)
{
if($this->CategoryID == $Data[$i][‘ID’])
{
$Str .= “<option value='{$Data[$i][‘ID’]}’ selected>”
. str_repeat(” “, $floor * 3)
. ‘├’
. ($ProcessFunc ? $ProcessFunc($Data[$i]) :
$Data[$i][‘CategoryName’])
. “</option>n”;
}
else
{
$Str .= “<option value='{$Data[$i][‘ID’]}’>”
. str_repeat(” “, $floor * 3)
. ‘├’
. ($ProcessFunc ? $ProcessFunc($Data[$i]) :
$Data[$i][‘CategoryName’])
. “</option>n”;
}

if($Data[$i][‘Child’]) $Str .=
$this->GenerateTypeTreeList($Data[$i][‘Child’], $ProcessFunc,
$floor + 1);
}

return $Str;
}

//—————————————————————————
//public String GenerateTypeTreeView(array $Data, String $ProcessFunc =
”)
// 返回整个分类的树状结构视图
//
// @param: $Data 此参数由 $this->DumpTypeDataToTree() 返回
//
// @param: $ProcessFunc 处理显示分类信息的回调函数, 函数原型请参照:
$this->PrintCurrentLevel()
//
// return value: 返回生成的一颗HTML形式显示的树
//
//—————————————————————————
function GenerateTypeTreeView($Data, $ProcessFunc)
{
$Str = ‘<ul style=”Line-Height:200%”>’;

for($i = 0, $cnt = Count($Data); $i < $cnt; $i++)
{
if($ProcessFunc) $Str .= ‘<li>’ . $ProcessFunc($Data[$i]) .
‘</li>’ . “n”;
else $Str .= ‘<li>’ . $Data[$i][‘CategoryName’] .
‘</li>’ . “n”;

if($Data[$i][‘Child’]) $Str .= ‘<li>’ .
$this->GenerateTypeTreeView($Data[$i][‘Child’], $ProcessFunc) .
‘</li>’;
}

$Str .= ‘</ul>’;

return $Str;
}

//—————————————————————————
//public String PrintCurrentLevel(String $ProcessFunc = ”)
// 对多级分类生成当前位置字符串
// 设如分类数据如下,当前分类为3级分类, 则调用返回 1级分类 -> 2级分类
-> 3级分类
// ├──1级分类
// │
// │
// │
// ├─2级分类
// │ │
// │ └─3级分类
// │
// └─2级分类
//
//
//
//
// @param: $ProcessFunc
此为对分类数据如何显示的回调函数,不设置则直接显示分类名称
// 函数定义原型为 function (&$arr);
// 其中$arr参数为每一个分类信息的一维数组如下:
// array(ID => 1, ParentID => 0, RootID => 0, CategoryName
=> ‘1级分类’)
//
返回值为对上述数据处理的结果,比如返回带链接的分类名字、更改显示颜色等
//
// return value: 返回当前分类在整个分类树中所处位置
//
//—————————————————————————
function PrintCurrentLevel($ProcessFunc = ”)
{
if(!$this->CategoryID) return ”;

if($this->_SelfData->Get(“RootID”) == 0)
{
if($ProcessFunc) return
$ProcessFunc($this->_SelfData->fetchDataToArray());
else return $this->_SelfData->Get(“CategoryName”);
}

$Current = $this->CategoryID;

$this->tblObj->SetCondition(‘RootID = ‘ .
$this->_SelfData->Get(‘RootID’) . ” or ID = ” .
$this->_SelfData->Get(‘RootID’));

$Data = $this->tblObj->MapResult($this->tblObj->Select());

return $this->_CurrentLevel($Data, $Current, $ProcessFunc);
}

//—————————————————————————
//public boolean Add(array $arr)
// 添加新分类到分类表中
//
// @param: $arr 在此数组中包括对新添加分类的定义, 定义如下:
//
// $arr[‘RootID’] 新分类所属的根分类ID
// $arr[‘ParentID’] 新分类的父分类ID
// $arr[‘CategoryName’] 新分类的名称
//
// return value: 返回添加分类操作结果
//
//—————————————————————————
function Add($arr)
{
$this->tblObj->SetFields(
Array(
‘RootID’,
‘ParentID’,
‘CategoryName’,
)
);

return $this->tblObj->Insert(
Array(
$arr[‘RootID’],
$arr[‘ParentID’],
$arr[‘CategoryName’],
)
);
}

//—————————————————————————
//public boolean Delete(int $ID)
// 删除已经存在的分类
//
// @param: $ID 要删除的分类ID
//
// return value: 返回删除分类操作结果
//
//—————————————————————————
function Delete($ID)
{
$sysOption = &$this->KernelRef->Config;

$this->tblObj->SetFields(‘*’);
$this->tblObj->SetCondition(‘ID = ‘ . (int)$ID);

return $this->tblObj->Delete();
}

//—————————————————————————
//public boolean Modify(int $ID, array $arr)
// 修改已经存在的分类
//
// @param: $ID 要修改的分类ID
// @param: $arr 在此数组中包括修改后的分类定义, 定义如下:
//
// $arr[‘RootID’] 新分类所属的根分类ID
// $arr[‘ParentID’] 新分类的父分类ID
// $arr[‘CategoryName’] 新分类的名称
//
// return value: 返回修改分类操作结果
//
//—————————————————————————
function Modify($ID, $arr)
{
$this->tblObj->SetCondition(‘ID = ‘ . (int)$ID);

$prev = $this->tblObj->MapOneRow($this->tblObj->Select());

$this->tblObj->SetFields(
Array(
‘RootID’,
‘ParentID’,
‘CategoryName’,
)
);

return $this->tblObj->Update($arr);
}

//—————————————————————————
//public array Modify(int $ID)
// 修改已经存在的分类
//
// @param: $ID 指定的分类ID
//
// return value: 返回指定ID分类的信息
// 数组中包括:
// Array(
// ‘ID’ => 分类ID,
// ‘ParentID’ => 父分类ID,
// ‘RootID’ => 根分类ID,
// ‘CategoryName’ => 分类名称,
// );
//
//—————————————————————————
function GetCategory($ID)
{
$this->tblObj->SetCondition(‘ID = ‘ . (int)$ID);

return $this->tblObj->MapOneRow($this->tblObj->Select());
}
}
?>