php实现无限级树型菜单

风清扬斈 8年前 (2011-10-11) 信息科技 5391 0

!!写递归函数,可考虑缓存,定义一些静态变量来存上一次运行的结果,多程序运行效率很有帮助.

大概步骤如下:
首先到数据库取数据,放到一个数组,
然后把数据转化为一个树型状的数组,
最后把这个树型状的数组转为html代码。
也可以将第二步和第三步合为一步。

详细如下:
1。数据库设计:
脚本如下:
CREATE TABLE `bg_cate` (
`cate_Id` int(30) unsigned NOT NULL AUTO_INCREMENT,
`cate_ParentId` int(30) unsigned DEFAULT (i)0(i),
`cate_Name` varchar(100) NOT NULL,
`cate_Intro` varchar(500) DEFAULT NULL,
`cate_Order` int(30) unsigned DEFAULT (i)0(i),
`cate_Icon` varchar(100) DEFAULT NULL,
PRIMARY KEY (`cate_Id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=34 ;

--
-- 导出表中的数据 `bg_cate`
--

INSERT INTO `bg_cate` (`cate_Id`, `cate_ParentId`, `cate_Name`, `cate_Intro`, `cate_Order`, `cate_Icon`) VALUES
(4, 0, (i)往事如风(i), (i)记录往事(i), 0, (i)icons/6.gif(i)),
(5, 0, (i)水煮三国(i), (i)品位三国智慧(i), 0, (i)icons/3.gif(i)),
(2, 0, (i)技术学习(i), (i)平时学习的一些笔记,欢迎批评指正。(i), 0, (i)icons/18.gif(i)),
(3, 0, (i)生活点滴(i), (i)记录生活点滴(i), 0, (i)icons/2.gif(i)),
(6, 0, (i)栀子花开(i), (i)青春无限(i), 0, (i)icons/8.gif(i)),
(7, 0, (i)假日休闲(i), (i)悠闲、自在(i), 0, (i)icons/24.gif(i)),
(8, 2, (i)html(i), (i)html学习(i), 0, (i)icons/1.gif(i)),
(9, 2, (i)css(i), (i)css学习(i), 0, (i)icons/1.gif(i)),
(10, 2, (i)php(i), (i)php学习(i), 0, (i)icons/18.gif(i)),
(11, 10, (i)php基础知识(i), (i)php基础知识(i), 0, (i)icons/1.gif(i)),
(12, 10, (i)oop(i), (i)oop(i), 0, (i)icons/1.gif(i)),
(13, 10, (i)php安全(i), (i)讲述php安全(i), 0, (i)icons/1.gif(i)),
(14, 10, (i)seagull framework(i), (i)seagull framework(i), 0, (i)icons/1.gif(i)),
(15, 2, (i)javascript(i), (i)javascript学习(i), 0, (i)icons/1.gif(i)),
(16, 2, (i)设计模式(i), NULL, 0, (i)icons/1.gif(i)),
(17, 2, (i)软件工程(i), (i)软件工程学习(i), 0, (i)icons/1.gif(i)),
(18, 3, (i)厦门生活(i), (i)厦门生活(i), 0, (i)icons/8.gif(i)),
(19, 3, (i)大学生活(i), (i)大学生活(i), 0, (i)icons/8.gif(i)),
(20, 3, (i)童年生活(i), (i)童年生活(i), 0, (i)icons/15.gif(i)),
(21, 19, (i)学习(i), (i)学习(i), 0, (i)icons/1.gif(i)),
(22, 19, (i)运动(i), (i)运动(i), 0, (i)icons/16.gif(i)),
(23, 19, (i)旅游(i), (i)旅游(i), 0, (i)icons/24.gif(i)),
(24, 22, (i)排球(i), (i)排球(i), 0, (i)icons/9.gif(i)),
(25, 22, (i)篮球(i), (i)篮球(i), 0, (i)icons/9.gif(i)),
(26, 22, (i)羽毛球(i), (i)羽毛球(i), 0, (i)icons/9.gif(i)),
(27, 22, (i)乒乓球(i), (i)乒乓球(i), 0, (i)icons/9.gif(i));


2。到数据库取数据,放到数组。
require_once (i)./classes/MyDB.php(i);
$con = MyDB::singleton();
$sql = <<<SQL
    select * from bg_cate cate
SQL;
$data = $con->getAll($sql);
//print_r($data);
数据库操作我用的是pear类库。
最后的$data的数据格式如下:
Array
(
    [0] => Array
        (
            [cate_Id] => 4
            [cate_ParentId] => 0
            [cate_Name] => 往事如风
            [cate_Intro] => 记录往事
            [cate_Order] => 0
            [cate_Icon] => icons/6.gif
        )

    [1] => Array
        (
            [cate_Id] => 5
            [cate_ParentId] => 0
            [cate_Name] => 水煮三国
            [cate_Intro] => 品位三国智慧
            [cate_Order] => 0
            [cate_Icon] => icons/3.gif
        )
。。。。。。


3。把上一步的数据转为树型状的数组
代码如下:
function getTree($data, $pId)
{
$tree = (i)(i);
foreach($data as $k => $v)
{
   if($v[(i)cate_ParentId(i)] == $pId)
   {         //父亲找到儿子
    $v[(i)cate_ParentId(i)] = getTree($data, $v[(i)cate_Id(i)]);
    $tree[] = $v;
    //unset($data[$k]);
   }
}
return $tree;
}
$tree = getTree($data, 0);
最后输出$tree的数据格式为:
Array
(
    [0] => Array
        (
            [cate_Id] => 4
            [cate_ParentId] =>
            [cate_Name] => 往事如风
            [cate_Intro] => 记录往事
            [cate_Order] => 0
            [cate_Icon] => icons/6.gif
        )

    [1] => Array
        (
            [cate_Id] => 5
            [cate_ParentId] =>
            [cate_Name] => 水煮三国
            [cate_Intro] => 品位三国智慧
            [cate_Order] => 0
            [cate_Icon] => icons/3.gif
        )

    [2] => Array
        (
            [cate_Id] => 2
            [cate_ParentId] => Array
                (
                    [0] => Array
                        (
                            [cate_Id] => 8
                            [cate_ParentId] =>
                            [cate_Name] => html
                            [cate_Intro] => html学习
                            [cate_Order] => 0
                            [cate_Icon] => icons/1.gif
                        )
。。。。。。。。。。。


4。把树型状数组转为html
代码如下:
function procHtml($tree)
{
$html = (i)(i);
foreach($tree as $t)
{
   if($t[(i)cate_ParentId(i)] == (i)(i))
   {
    $html .= "<li>{$t[(i)cate_Name(i)]}</li>";
   }
   else
   {
    $html .= "<li>".$t[(i)cate_Name(i)];
    $html .= procHtml($t[(i)cate_ParentId(i)]);
    $html = $html."</li>";
   }
}
return $html ? (i)<ul>(i).$html.(i)</ul>(i) : $html ;
}
echo procHtml($tree);
输出的html的代码格式为:
<ul>
<li>往事如风</li>
<li>水煮三国</li>
<li>技术学习
   <ul>
    <li>html</li>
    <li>css</li>
    <li>php
     <ul>
      <li>php基础知识</li>
      <li>oop</li>
      <li>php安全</li>
。。。。。。。。。。。。。。。。

5。也可以把第3和第4步的代码合在一起,代码如下:
function getTree($data, $pId)
{
$html = (i)(i);
foreach($data as $k => $v)
{
   if($v[(i)cate_ParentId(i)] == $pId)
   {         //父亲找到儿子
    $html .= "<li>".$v[(i)cate_Name(i)];
    $html .= getTree($data, $v[(i)cate_Id(i)]);
    $html = $html."</li>";
   }
}
return $html ? (i)<ul>(i).$html.(i)</ul>(i) : $html ;
}
echo getTree($data, 0);

6。最后再加点css样式,效果如下:

小例子:

<?php
header("content-type: text/html; charset=utf-8");
$connect = mysql_connect((i)localhost(i), (i)root(i), (i)123456(i));
mysql_select_db("wz");
mysql_query("set names (i)utf8(i)");
//获得顶节点
$sql = "select id, name,ischild from tree where parent = 0 order by id asc";
$result = mysql_query($sql);
while($row = mysql_fetch_array($result))
{
extract($row);
//如果有子节点,就在前面加个事件,以便展开或者关闭子节点
$icon = $ischild ? "<a href=(i)javascript:expand("div$id");(i)>+</a>":"-";
$name = $icon . $name;
echo "<div id=(i)div" . $id . "(i)>" . $name;
if($ischild)
{
//递归获取节点
getNode($id, 0);
}
echo "</div>";
}

function getNode($id, $level)
{
$sql = "select id, name, ischild from tree where parent = $id order by id asc ";
$result = mysql_query($sql);
$level++;
while($row = mysql_fetch_array($result))
{
extract($row);
$icon = $ischild ? "<a href=(i)javascript:expand("div$id");(i)>+</a>":"-";
$name = $icon . $name;
echo "<div id=(i)div" . $id . "(i)>" . echoChar(" ", $level) . $name;
if($ischild)
{
getNode($id, $level);
}
echo "</div>";
}
}

function echoChar($char, $num)
{
for($i=0;$i< $num; $i++)
{
$strChar .= $char;
}
return $strChar;
}
?>
<script language="javascript">
function expand(id)
{
var obj = document.getElementById(id).childNodes;
for(var i=0;i<obj.length;i++)
{
if(obj[i].nodeName == "DIV")
{
switch(obj[i].style.display)
{
case "":
case "block":
obj[i].style.display = "none";
break;
case "none":
obj[i].style.display = "block";
break;
}
}
}
}
</script>


开发电子商务网站的时候,做了这个显示树形菜单的功能,用的递归。
写代码的时候,想破了脑袋,因为对递归了解的少,主要是在大学的时候,老师就说过了,能不用递归的时候就不要用递归。所以我一直都记着,在心里抵触着递归。递归是最没有效率的,这个我想每个搞开发的人都知道的。可是当我在搜索相关代码的时候,看到那些算法,我就头晕了,后来还是自己用递归写了个。用着感觉还蛮爽,可是后来因为某种原因,把它给XX了。
唉,发出来,就当纪念吧。哈哈,纪念版的PHP树形菜单函数。代码如下:

*/public function procCategory($sid,$pid){$return = array();$key = 0;static $arr = array(); //分类级别参考数组$sql = "select cid,pcid,name from shop_goods_catalog where sid=(i){$sid}(i) and pcid = (i){$pid}(i)";$result = $this->__db->query($sql); while($row=$this->__db->fetchArray($result)){$nbsp = (i)(i);if($row[(i)pcid(i)]==0){$arr = array();}$arr[] = $row[(i)pcid(i)];//顶级分类不添加树形结构标识。if($row[(i)pcid(i)]>0){//根据分类级别添加树形结构标识$key = array_search($row[(i)pcid(i)],$arr);for($i=0;$i<$key;$i++){$nbsp .= (i)&nbsp;&nbsp;(i);}//重构分类级别参考数组if(count($arr)>1&&count(array_keys($arr,$row[(i)pcid(i)]))>1){$arr = array_slice($arr,0,$key+1);}}$row[(i)name(i)] = $nbsp.$row[(i)name(i)];$row[(i)level(i)] = $key; //分类级别,0为顶级分类,1为二级分类,用于样式设定或其他需求$return[] = $row;$r = $this->procCategory($sid,$row[(i)cid(i)]);$return = array_merge($return,$r);} return $return;}
果注重程序效率的话,不要用此方法

<?php
/*
* 迭代sql查询,生成无限级分类树
* 数据结构
CREATE TABLE `cat` (
`cid` INT(5) UNSIGNED NOT NULL AUTO_INCREMENT,
`cname` VARCHAR(50) NOT NULL,
`lev` INT(5) UNSIGNED NOT NULL COMMENT (i)分类层数level(i),
`pid` INT(5) UNSIGNED NOT NULL COMMENT (i)上级cid(i),
PRIMARY KEY (`cid`)
)
* 本程序仅供演示“目录树”的算法概念,未做参数有效性的校验
* 本程序含有添加、删除、列目录树功能,编辑功能未实现(非常简单啦,你们懂得)
*/
error_reporting(7);
header("charset=utf-8");
mysql_connect("localhost", "root", "root");
mysql_select_db("cat");
mysql_query("set names utf8");

$act = $_GET[(i)act(i)];
if($act == "insert") {
$cname = trim($_POST[(i)cname(i)]);
$pid = intval($_POST[(i)cid(i)]);

//取得上级目录的分类层数
$query = "select * from cat where cid=(i)$pid(i)";
$res = mysql_query($query);
$row = mysql_fetch_assoc($res);
$parent_lev = $row[(i)lev(i)];

$query = "insert into cat set cname=(i)$cname(i),lev=(i)".($parent_lev+1)."(i),pid=(i)$pid(i)";
mysql_query($query);

echo "<script>alert((i)增加新分类成功(i));";
echo "location.href=(i)cat.php(i);</script>";
die;
}elseif($act == "del") {
$cid = intval($_GET[(i)cid(i)]);

if(delCat($cid)) {
echo "<script>alert((i)分类树删除成功(i));";
echo "location.href=(i)cat.php(i);</script>";
die;
}
}elseif($act == "edit") {
$cid = intval($_GET[(i)cid(i)]);

$query = "select * from cat where cid=(i)$cid(i)";
$res = mysql_query($query);
$editRow = mysql_fetch_assoc($res);

//将当前分类及其子分类的cid全部获得,在select中要屏蔽(disabled)
$pid = $editRow[(i)cid(i)];
$subArr = getCat($pid);
//提取cid
$subCidArr = array();
foreach($subArr as $_row) {
$subCidArr[] = $_row[(i)cid(i)];
}
array_unshift($subCidArr, $editRow[(i)cid(i)]);//所有子分类cid的数组头部压入当前编辑分类的cid,形成当前分类及其所属所有子分类的cid数组


$pid = 0;//从根分类开始列目录树
$okArr = getCat($pid);
?>
<form method="post" action="cat.php?act=update">
分类名:<input type="text" name="cname" value="<?php echo($editRow[(i)cname(i)]);?>"><br><br>
所属分类:<select name="cid">
<option value="0">根目录</option>
<?php
foreach($okArr as $row) {
echo "<option value=(i)".$row[(i)cid(i)]."(i)";
if($row[(i)cid(i)] == $editRow[(i)pid(i)]) {//将该分类的原始父分类设为默认选中状态
echo "selected ";
}
if(in_array($row[(i)cid(i)], $subCidArr)) {//将该分类自己及其子分类屏蔽
echo "disabled ";
}
echo ">";
for($i=1;$i<$row[(i)lev(i)];$i++) {
echo " ";
}
echo $row[(i)cname(i)];
echo "</option>";
}
?>
</select><br><br>
<input type="submit" name="提交">
<input type="hidden" name="edit_cid" value="<?php echo($cid);?>">
</form>
<?php
}elseif($act == "update") {
$cid = intval($_POST[(i)edit_cid(i)]);

$cname = trim($_POST[(i)cname(i)]);//新的分类名
$pid = intval($_POST[(i)cid(i)]);//新的父分类

//原始分类记录
$query = "select * from cat where cid=(i)$cid(i)";
$res = mysql_query($query);
$cat_old_row = mysql_fetch_assoc($res);

//新的父分类记录
$query = "select * from cat where cid=(i)$pid(i)";
$res = mysql_query($query);
$cat_p_row = mysql_fetch_assoc($res);

//比对原始记录的lev和将所属新父分类后的lev
$lev_old = $cat_old_row[(i)lev(i)];
$lev_new = $cat_p_row[(i)lev(i)]+1;
$lev_diff = $lev_old - $lev_new;//调整后的lev差值

//获得所有子分类
$subArr = getCat($cid);
//提取所有子分类的cid
$subCidArr = array();
foreach($subArr as $_row) {
$subCidArr[] = $_row[(i)cid(i)];
}
array_unshift($subCidArr, $cid);//所有子分类cid的数组头部压入当前编辑分类的cid,形成当前分类及其所属所有子分类的cid数组

//修改当前分类记录信息
$query1 = "update cat set cname=(i)$cname(i), pid=(i)$pid(i) where cid=(i)$cid(i)";
mysql_query($query1);

//并当前分类及其所有子分类的lev做相应的差值调整
$query2 = "update cat set lev=lev-$lev_diff where cid in (".implode(",", $subCidArr).")";
mysql_query($query2);

echo "<script>alert((i)分类编辑成功(i));";
echo "location.href=(i)cat.php(i);</script>";
die;
}else{
/* 按照分类层次等级列目录树 */


$pid = 0;//从根分类开始列目录树
$catAllArr = getCat($pid);


echo "<h2>目录树</h2>";
foreach($catAllArr as $row) {
for($i=1;$i<$row[(i)lev(i)];$i++) {
echo " ";
}
echo $row[(i)cname(i)]." <a href=(i)cat.php?act=edit&cid=".$row[(i)cid(i)]."(i)>编辑</a> <a href=(i)?act=del&cid=".$row[(i)cid(i)]."(i)>删除</a><BR>";
}

echo "<HR>";
echo "<h4>创建新分类</h4>";
?>
<meta charset="utf-8">
<form method="post" action="cat.php?act=insert">
分类名:<input type="text" name="cname"><br><br>
所属分类:<select name="cid">
<option value="0">根目录</option>
<?php
foreach($catAllArr as $row) {
echo "<option value=(i)".$row[(i)cid(i)]."(i)>";
for($i=1;$i<$row[(i)lev(i)];$i++) {
echo " ";
}
echo $row[(i)cname(i)];
echo "</option>";
}
?>
</select><br><br>
<input type="submit" name="提交">
</form>
<?php

}

//递归获得分类目录树
function getCat($pid, &$arr="") {
if(empty($arr)) {
$arr = array();
}

$sql = "select * from cat where pid=(i)$pid(i)";
$res = mysql_query($sql);

while($row = mysql_fetch_assoc($res)) {
if(!empty($row)) {
$arr[] = $row;

getCat($row[(i)cid(i)], $arr);
}
}
return $arr;
}

//递归删除分类目录树
function delCat($cid) {
//获得下一层分类
$sql = "select * from cat where pid=(i)$cid(i)";
$res = mysql_query($sql);

while($row = mysql_fetch_assoc($res)) {
if(!empty($row)) {
delCat($row[(i)cid(i)]);
}
}

//删除本层分类
$sql = "delete from cat where cid=(i)$cid(i)";
$res = mysql_query($sql);

return $res;
}

"

相关推荐

  • 网友评论

    • (*)

    最新评论