我想在PHP和MySQL中从我的数据库中构建一个无序列表菜单树。PHP/MySQL构建树形菜单
我有一个页面对象数组,我从数据库返回。每个页面对象都有parent_id属性,如果它没有父项,则设置为null。这里的页面对象是什么样子:
page object
id
title
parent_id
如果可能的话,我想这样做递归仅命中一次数据库,因为我将要建立在几乎每个请求的菜单。我想创建一个函数,我可以传递我的数组对象,它将返回html列表。
我想在PHP和MySQL中从我的数据库中构建一个无序列表菜单树。PHP/MySQL构建树形菜单
我有一个页面对象数组,我从数据库返回。每个页面对象都有parent_id属性,如果它没有父项,则设置为null。这里的页面对象是什么样子:
page object
id
title
parent_id
如果可能的话,我想这样做递归仅命中一次数据库,因为我将要建立在几乎每个请求的菜单。我想创建一个函数,我可以传递我的数组对象,它将返回html列表。
我喜欢@马里奥的解决方案,并在预防多余的<ul>
方面有所改进。我只想建议这样做对你的SQL查询的ORDER BY
得到你想要的(订单甚至可能建议重量/顺序列在菜单中添加到架构
数据设置:
$menu = array(// Presumed to have been coming from a SQL SELECT, populated for demo.
array('id'=>1,'title'=>'Menu 1', 'parent_id'=>null),
array('id'=>2,'title'=>'Sub 1.1', 'parent_id'=>1),
array('id'=>3,'title'=>'Sub 1.2', 'parent_id'=>1),
array('id'=>4,'title'=>'Sub 1.3', 'parent_id'=>1),
array('id'=>5,'title'=>'Menu 2', 'parent_id'=>null),
array('id'=>6,'title'=>'Sub 2.1', 'parent_id'=>5),
array('id'=>7,'title'=>'Sub Sub 2.1.1', 'parent_id'=>6),
array('id'=>8,'title'=>'Sub 2.2', 'parent_id'=>5),
array('id'=>9,'title'=>'Menu 3', 'parent_id'=>null),
);
处理:
function has_children($rows,$id) {
foreach ($rows as $row) {
if ($row['parent_id'] == $id)
return true;
}
return false;
}
function build_menu($rows,$parent=0)
{
$result = "<ul>";
foreach ($rows as $row)
{
if ($row['parent_id'] == $parent){
$result.= "<li>{$row['title']}";
if (has_children($rows,$row['id']))
$result.= build_menu($rows,$row['id']);
$result.= "</li>";
}
}
$result.= "</ul>";
return $result;
}
echo build_menu($menu);
输出:
<ul>
<li>Menu 1<ul>
<li>Sub 1.1</li>
<li>Sub 1.2</li>
<li>Sub 1.3</li>
</ul></li>
<li>Menu 2<ul>
<li>Sub 2.1<ul>
<li>Sub Sub 2.1.1</li>
</ul></li>
<li>Sub 2.2</li>
</ul></li>
<li>Menu 3</li>
</ul>
而不是递归查询数据库,您可以拉出所有条目并使输出函数递归。这往往是微不足道的:
function print_list($array, $parent=0) {
print "<ul>";
foreach ($array as $row) {
if ($row->parent_id == $parent) {
print "<li>$row->title";
print_list($array, $row->id); # recurse
print "</li>";
} }
print "</ul>";
}
这只是筑巢重要<ul>
s转换<li>
。或者只是使用HTML,并关闭</li>
。
其实这打印太多<ul>
s,所以我会检查sublevels的存在,并避免直接打印它。
我结束了该溶液(pastebin reference)去:
<?php
/**
* Generate HTML for multi-dimensional menu from MySQL database
* with ONE QUERY and WITHOUT RECURSION
* @author J. Bruni
*/
class MenuBuilder
{
/**
* MySQL connection
*/
var $conn;
/**
* Menu items
*/
var $items = array();
/**
* HTML contents
*/
var $html = array();
/**
* Create MySQL connection
*/
function MenuBuilder()
{
$this->conn = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('example', $this->conn);
}
/**
* Perform MySQL query and return all results
*/
function fetch_assoc_all($sql)
{
$result = mysql_query($sql, $this->conn);
if (!$result)
return false;
$assoc_all = array();
while($fetch = mysql_fetch_assoc($result))
$assoc_all[] = $fetch;
mysql_free_result($result);
return $assoc_all;
}
/**
* Get all menu items from database
*/
function get_menu_items()
{
// Change the field names and the table name in the query below to match tour needs
$sql = 'SELECT id, parent_id, title, link, position FROM menu_item ORDER BY parent_id, position;';
return $this->fetch_assoc_all($sql);
}
/**
* Build the HTML for the menu
*/
function get_menu_html($root_id = 0)
{
$this->html = array();
$this->items = $this->get_menu_items();
foreach ($this->items as $item)
$children[$item['parent_id']][] = $item;
// loop will be false if the root has no children (i.e., an empty menu!)
$loop = !empty($children[$root_id]);
// initializing $parent as the root
$parent = $root_id;
$parent_stack = array();
// HTML wrapper for the menu (open)
$this->html[] = '<ul>';
while ($loop && (($option = each($children[$parent])) || ($parent > $root_id)))
{
if ($option === false)
{
$parent = array_pop($parent_stack);
// HTML for menu item containing childrens (close)
$this->html[] = str_repeat("\t", (count($parent_stack) + 1) * 2) . '</ul>';
$this->html[] = str_repeat("\t", (count($parent_stack) + 1) * 2 - 1) . '</li>';
}
elseif (!empty($children[$option['value']['id']]))
{
$tab = str_repeat("\t", (count($parent_stack) + 1) * 2 - 1);
// HTML for menu item containing childrens (open)
$this->html[] = sprintf(
'%1$s<li><a href="%2$s">%3$s</a>',
$tab, // %1$s = tabulation
$option['value']['link'], // %2$s = link (URL)
$option['value']['title'] // %3$s = title
);
$this->html[] = $tab . "\t" . '<ul class="submenu">';
array_push($parent_stack, $option['value']['parent_id']);
$parent = $option['value']['id'];
}
else
// HTML for menu item with no children (aka "leaf")
$this->html[] = sprintf(
'%1$s<li><a href="%2$s">%3$s</a></li>',
str_repeat("\t", (count($parent_stack) + 1) * 2 - 1), // %1$s = tabulation
$option['value']['link'], // %2$s = link (URL)
$option['value']['title'] // %3$s = title
);
}
// HTML wrapper for the menu (close)
$this->html[] = '</ul>';
return implode("\r\n", $this->html);
}
}
实施例的数据:
CREATE TABLE `menu_item` (
`id` int(11) NOT NULL,
`title` varchar(75) DEFAULT NULL,
`link` varchar(100) DEFAULT NULL,
`parent_id` int(11) DEFAULT NULL,
`position` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `menu_item` (`id`, `title`, `link`, `parent_id`, `position`) VALUES (1,'1','1.html',0,1);
INSERT INTO `menu_item` (`id`, `title`, `link`, `parent_id`, `position`) VALUES (2,'2','2.html',0,2);
INSERT INTO `menu_item` (`id`, `title`, `link`, `parent_id`, `position`) VALUES (3,'11','11.html',1,1);
INSERT INTO `menu_item` (`id`, `title`, `link`, `parent_id`, `position`) VALUES (4,'12','12.html',1,2);
INSERT INTO `menu_item` (`id`, `title`, `link`, `parent_id`, `position`) VALUES (5,'21','21.html',2,1);
INSERT INTO `menu_item` (`id`, `title`, `link`, `parent_id`, `position`) VALUES (6,'22','22.html',2,2);
INSERT INTO `menu_item` (`id`, `title`, `link`, `parent_id`, `position`) VALUES (7,'3','3.html',0,3);
用法:
$menu = new MenuBuilder();
echo '<pre>' . htmlentities($menu->get_menu_html()) . '</pŕe>';
我的pastebin在这里结束......我在7年后承认它......很好。 – 2017-06-19 13:07:47
@ J.Bruni欢迎来到互联网 – jQuerybeast 2017-09-03 18:55:42
这不输出正确的结果为了我。我更新它使用对象而不是数组,但是这不应该改变任何东西。 – imns 2010-12-10 23:08:20
print_r你传递的数组,并确保它与我所展示的相似。我假设你会执行查询,并将每个'..._ fetch_array'添加到列表中并使用它(或者重新创建一个模仿我提供的项目)。 – 2010-12-10 23:45:22