php 和 mysql 目录样式数据库中的子菜单
Submenus in php and mysql directory style database
我一直在尝试在每个主要项目上制作一个带有子菜单的菜单,然后通过 php 和 mysql 在每个子菜单上制作 sub_sub_menus。我觉得我快到了,但我是 php 和 mysql 的新手,所以我来这里寻求您的帮助。
我花了几个小时寻找答案,我找到了下拉菜单的示例,但 none 是这样的。
不确定如何在此处添加 sql 数据库,但会根据要求尝试添加。
我的 php 代码如下。
$result = mysqli_query($link, 'SELECT * FROM library WHERE parent_id =' . $parent_id = 0);
$subResult = mysqli_query($link, 'SELECT * FROM library WHERE parent_id >'. $parent_id);
if(mysqli_num_rows($result) > 0) {
echo '<ul class="nav list">';
while($row = mysqli_fetch_array($result))
{
echo '<li class="nav-item">'. '<a href="#">'.$row['mainfolder'] .'</a>'.
'<ul class="dropdown-content nav">';
while($sub = mysqli_fetch_array($subResult))
{
if($row['id']===$sub['parent_id'])
{
echo $sub['parent_id'];
$submenuItems = mysqli_query($link, 'SELECT mainfolder FROM library WHERE parent_id ='. $sub['parent_id']);
$items = mysqli_fetch_array($submenuItems);
foreach ($items as $eachItem){
echo' <li class="submenu">'.'<a class="sub" href="#">'.htmlspecialchars($eachItem, ENT_QUOTES, 'UTF-8').'</li>'.'</a>'.'</ul>';
}
}
}
}
echo '</ul>'.'</li>';
echo '</ul>';
}
因为你问得很好,也因为我喜欢递归函数的挑战,所以我决定继续帮助你。
我原来的答案有点失控,所以我整理了一下。我还进行了大量调试,并修复了我在原始 post.
中发现的一些 "bugs"
echo "<pre>"; //preserve whitespace
//I mocked up some arrays ('canned data') to make things easier to show and test
//order of the parents matter ( should be in the order left to right of the menu )
$parents = [
['id' => 1, 'title' => 'item1', 'link' => 'http://www.example.com/item1', 'parent_id' => 0],
['id' => 8, 'title' => 'item8', 'link' => 'http://www.example.com/item8', 'parent_id' => 0],
];
var_export( $parents );
echo "\n\n";
//you can order children any way you want, they will still be grouped under the right parent
$children = [
['id' => 2, 'title' => 'item2', 'link' => 'http://www.example.com/item2', 'parent_id' => 1],
['id' => 3, 'title' => 'item3', 'link' => 'http://www.example.com/item3', 'parent_id' => 2],
['id' => 4, 'title' => 'item4', 'link' => 'http://www.example.com/item4', 'parent_id' => 1], //not ordered
['id' => 5, 'title' => 'item5', 'link' => 'http://www.example.com/item5', 'parent_id' => 2, "target" => null, "css_class" => null],
['id' => 6, 'title' => 'item6', 'link' => 'http://www.example.com/item6', 'parent_id' => 1, "target" => "_blank"],
['id' => 7, 'title' => 'item7', 'link' => 'http://www.example.com/item7', 'parent_id' => 8, "css_class"=>"test"],
];
var_export($children);
echo "\n\n";
$lv = 1;
$html = "<ul class=\"menu menu_lv0\" >\n";
foreach( $parents AS &$parent ){
$html = buildMenuTree( $parent, $children, $html, $lv, $lv ); //pass by refrence so no return needed
}
$html .= "</ul>\n";
var_export( $parents );
echo "\n\n";
echo htmlspecialchars( $html )."\n\n"; //htmlspecialchars for display reasons
//=========================================
// DEFINE FUNCTION
//=========================================
//$parent is passed by refrence
function buildMenuTree( &$parent, $children, $html='', $lv=0, $indent=0 ){
if( $lv == 1 || $lv == 2) $mode = true;
if( !$children ) return;
reset( $children ); //reset the array
$t0 = getIndent( $indent );
$t1 = getIndent( ($indent+1) );
//css class for menu item
$css_class = (isset($parent['css_class']) && $parent['css_class']) ? " {$parent['css_class']}" : "";
//link target
$target = (isset($parent['target']) && $parent['target']) ? "target=\"{$parent['target']}\" " : "";
$id = $parent['id'];
$html .= "{$t0}<li id=\"menu_item_{$id}\" class=\"menu_item item_lv{$lv}{$css_class}\">\n";
$html .= "{$t1}<a class=\"menu_link\" href=\"{$parent['link']}\" {$target}>{$parent['title']}</a>";
while( false !== ( $child = current( $children ) ) ){
//if the parents id, matches the childs parent id, then add the current child
//as a child of parent and check it ( current child ) for any children it may have
// add if( ... && $limit == $lv) and pass in a $limit param to the function to limit the depth
if( $parent['id'] == $child['parent_id'] ){
$key = key( $children );
//remove - to reduce array and our processing time, we can remove the current item
//also the current item is the parent in the recusive function and therefore we
//know it cannot be a child of itself and we wont need it in that branch
unset( $children[$key] );
//make a recursive call ( call this method again
//&$parent, $children, $html='', $lv=1, $indent=1
$r_html = buildMenuTree($child, $children, "", ($lv + 1), ($indent+2));
//creante the children array if not exists
if( !isset( $parent['children'] ) ){
$parent['children'] = [];
$html .= "\n{$t1}<ul class=\"sub_menu menu_lv{$lv} parent_{$parent['id']}\" >\n";
$html .= $r_html;
}else{
$html .= $r_html;
}
///store the child
$parent['children'][] = $child;
}else{
$next = next( $children );
};
}
if( !isset( $parent['children'] ) )
$html .= "\n";
else
$html .= "{$t1}</ul>\n";
$html .= "{$t0}</li>\n";
return $html;
}
function getIndent( $indent, $pad = " " ){
return str_pad("", ( $indent * 4 ), $pad);
}
echo "</pre>";
我在 Apache/2.4.18 (Win32) PHP/7.0.4) 上测试过,100% 有效。
输出
数组树结构
Parent 是通过引用传递的,所以它的修改不是 returned,我们需要 return 用于 HTML
$parent = array (
0 =>
array (
'id' => 1,
'title' => 'item1',
'link' => 'http://www.example.com/item1',
'parent_id' => 0,
'children' =>
array (
0 =>
array (
'id' => 2,
'title' => 'item2',
'link' => 'http://www.example.com/item2',
'parent_id' => 1,
'children' =>
array (
0 =>
array (
'id' => 3,
'title' => 'item3',
'link' => 'http://www.example.com/item3',
'parent_id' => 2,
),
1 =>
array (
'id' => 5,
'title' => 'item5',
'link' => 'http://www.example.com/item5',
'parent_id' => 2,
'target' => NULL,
'css_class' => NULL,
),
),
),
1 =>
array (
'id' => 4,
'title' => 'item4',
'link' => 'http://www.example.com/item4',
'parent_id' => 1,
),
2 =>
array (
'id' => 6,
'title' => 'item6',
'link' => 'http://www.example.com/item6',
'parent_id' => 1,
'target' => '_blank',
),
),
),
1 =>
array (
'id' => 8,
'title' => 'item8',
'link' => 'http://www.example.com/item8',
'parent_id' => 0,
'children' =>
array (
0 =>
array (
'id' => 7,
'title' => 'item7',
'link' => 'http://www.example.com/item7',
'parent_id' => 8,
'css_class' => 'test',
),
),
),
)
HTML
<ul class="menu menu_lv0" >
<li id="menu_item_1" class="menu_item item_lv1">
<a class="menu_link" href="http://www.example.com/item1" >item1</a>
<ul class="sub_menu menu_lv1 parent_1" >
<li id="menu_item_2" class="menu_item item_lv2">
<a class="menu_link" href="http://www.example.com/item2" >item2</a>
<ul class="sub_menu menu_lv2 parent_2" >
<li id="menu_item_3" class="menu_item item_lv3">
<a class="menu_link" href="http://www.example.com/item3" >item3</a>
</li>
<li id="menu_item_5" class="menu_item item_lv3">
<a class="menu_link" href="http://www.example.com/item5" >item5</a>
</li>
</ul>
</li>
<li id="menu_item_4" class="menu_item item_lv2">
<a class="menu_link" href="http://www.example.com/item4" >item4</a>
</li>
<li id="menu_item_6" class="menu_item item_lv2">
<a class="menu_link" href="http://www.example.com/item6" target="_blank" >item6</a>
</li>
</ul>
</li>
<li id="menu_item_8" class="menu_item item_lv1">
<a class="menu_link" href="http://www.example.com/item8" >item8</a>
<ul class="sub_menu menu_lv1 parent_8" >
<li id="menu_item_7" class="menu_item item_lv2 test">
<a class="menu_link" href="http://www.example.com/item7" >item7</a>
</li>
</ul>
</li>
</ul>
我冒昧地添加了一些我认为是导航菜单通常实现的功能。
- CSS Class,如果一行包含
$row ['css_class']
,则 <li>
会将其值添加到 class 中,您可以将其设置为 false|null
或者只是没有。
- 目标,如果项目包含
$row ['target']
,则 <a>
标签将具有目标属性及其值,您可以将其设置为 false|null
或不设置。
你可以看到这些测试用例:
- 大多数项目省略
target
和css_class
我不喜欢使用class这个词,因为它是一个保留字
- item5 将
target
和 css_class
设置为 null
任何 false
值都将忽略这些标签。
- item6 的
target
值为 _blank
并且输出 中的 link <a target="_blank" ..
- item7 有
css_class
个 test
并且有这些 classes menu_item item_lv2 test
这是我添加到 HTML
的 classes 的列表
UL Classes:
- 顶级
<ul>
将有 class 的 menu
- 子级别
<ul>
将有 class 的 sub_menu
- 子级别
<ul>
将具有 parent_{id}
的 class 其中 {id}
是 parent
- Every
<ul>
将有一个 class of menu_lv{lv}
其中 {lv}
是嵌套级别
UL Classes & Id:
- Every
<li>
的 ID 为 menu_item_{id}
其中 {id}
是记录 ID
- 每个
<li>
将有 class 个 menu_item
- 每个
<li>
将有一个 class 的 item_lv{lv}
其中 {lv}
是嵌套级别
一个Classes:
- 每个
<a>
标签将有一个 class of menu_link
唯一的问题是不要在记录中手动设置 children
键,$parent['children']
或 $child['children']
。我不得不用它来检测 children 是否已经被添加。 (虽然可以通过一些工作来改进,但它似乎并不重要)
这需要大量的工作和时间,因此 CSS 您只能靠自己了。但是,我会说我已经构建了很多很多菜单,这个 HTML 应该非常接近您的需要(尽管我承认,我制作导航菜单已经有几年的感觉了)。
最后你可以用这个 link
在 PHPSanbox 中在线测试它
祝你好运,享受!
我一直在尝试在每个主要项目上制作一个带有子菜单的菜单,然后通过 php 和 mysql 在每个子菜单上制作 sub_sub_menus。我觉得我快到了,但我是 php 和 mysql 的新手,所以我来这里寻求您的帮助。 我花了几个小时寻找答案,我找到了下拉菜单的示例,但 none 是这样的。
不确定如何在此处添加 sql 数据库,但会根据要求尝试添加。
我的 php 代码如下。
$result = mysqli_query($link, 'SELECT * FROM library WHERE parent_id =' . $parent_id = 0);
$subResult = mysqli_query($link, 'SELECT * FROM library WHERE parent_id >'. $parent_id);
if(mysqli_num_rows($result) > 0) {
echo '<ul class="nav list">';
while($row = mysqli_fetch_array($result))
{
echo '<li class="nav-item">'. '<a href="#">'.$row['mainfolder'] .'</a>'.
'<ul class="dropdown-content nav">';
while($sub = mysqli_fetch_array($subResult))
{
if($row['id']===$sub['parent_id'])
{
echo $sub['parent_id'];
$submenuItems = mysqli_query($link, 'SELECT mainfolder FROM library WHERE parent_id ='. $sub['parent_id']);
$items = mysqli_fetch_array($submenuItems);
foreach ($items as $eachItem){
echo' <li class="submenu">'.'<a class="sub" href="#">'.htmlspecialchars($eachItem, ENT_QUOTES, 'UTF-8').'</li>'.'</a>'.'</ul>';
}
}
}
}
echo '</ul>'.'</li>';
echo '</ul>';
}
因为你问得很好,也因为我喜欢递归函数的挑战,所以我决定继续帮助你。
我原来的答案有点失控,所以我整理了一下。我还进行了大量调试,并修复了我在原始 post.
中发现的一些 "bugs"echo "<pre>"; //preserve whitespace
//I mocked up some arrays ('canned data') to make things easier to show and test
//order of the parents matter ( should be in the order left to right of the menu )
$parents = [
['id' => 1, 'title' => 'item1', 'link' => 'http://www.example.com/item1', 'parent_id' => 0],
['id' => 8, 'title' => 'item8', 'link' => 'http://www.example.com/item8', 'parent_id' => 0],
];
var_export( $parents );
echo "\n\n";
//you can order children any way you want, they will still be grouped under the right parent
$children = [
['id' => 2, 'title' => 'item2', 'link' => 'http://www.example.com/item2', 'parent_id' => 1],
['id' => 3, 'title' => 'item3', 'link' => 'http://www.example.com/item3', 'parent_id' => 2],
['id' => 4, 'title' => 'item4', 'link' => 'http://www.example.com/item4', 'parent_id' => 1], //not ordered
['id' => 5, 'title' => 'item5', 'link' => 'http://www.example.com/item5', 'parent_id' => 2, "target" => null, "css_class" => null],
['id' => 6, 'title' => 'item6', 'link' => 'http://www.example.com/item6', 'parent_id' => 1, "target" => "_blank"],
['id' => 7, 'title' => 'item7', 'link' => 'http://www.example.com/item7', 'parent_id' => 8, "css_class"=>"test"],
];
var_export($children);
echo "\n\n";
$lv = 1;
$html = "<ul class=\"menu menu_lv0\" >\n";
foreach( $parents AS &$parent ){
$html = buildMenuTree( $parent, $children, $html, $lv, $lv ); //pass by refrence so no return needed
}
$html .= "</ul>\n";
var_export( $parents );
echo "\n\n";
echo htmlspecialchars( $html )."\n\n"; //htmlspecialchars for display reasons
//=========================================
// DEFINE FUNCTION
//=========================================
//$parent is passed by refrence
function buildMenuTree( &$parent, $children, $html='', $lv=0, $indent=0 ){
if( $lv == 1 || $lv == 2) $mode = true;
if( !$children ) return;
reset( $children ); //reset the array
$t0 = getIndent( $indent );
$t1 = getIndent( ($indent+1) );
//css class for menu item
$css_class = (isset($parent['css_class']) && $parent['css_class']) ? " {$parent['css_class']}" : "";
//link target
$target = (isset($parent['target']) && $parent['target']) ? "target=\"{$parent['target']}\" " : "";
$id = $parent['id'];
$html .= "{$t0}<li id=\"menu_item_{$id}\" class=\"menu_item item_lv{$lv}{$css_class}\">\n";
$html .= "{$t1}<a class=\"menu_link\" href=\"{$parent['link']}\" {$target}>{$parent['title']}</a>";
while( false !== ( $child = current( $children ) ) ){
//if the parents id, matches the childs parent id, then add the current child
//as a child of parent and check it ( current child ) for any children it may have
// add if( ... && $limit == $lv) and pass in a $limit param to the function to limit the depth
if( $parent['id'] == $child['parent_id'] ){
$key = key( $children );
//remove - to reduce array and our processing time, we can remove the current item
//also the current item is the parent in the recusive function and therefore we
//know it cannot be a child of itself and we wont need it in that branch
unset( $children[$key] );
//make a recursive call ( call this method again
//&$parent, $children, $html='', $lv=1, $indent=1
$r_html = buildMenuTree($child, $children, "", ($lv + 1), ($indent+2));
//creante the children array if not exists
if( !isset( $parent['children'] ) ){
$parent['children'] = [];
$html .= "\n{$t1}<ul class=\"sub_menu menu_lv{$lv} parent_{$parent['id']}\" >\n";
$html .= $r_html;
}else{
$html .= $r_html;
}
///store the child
$parent['children'][] = $child;
}else{
$next = next( $children );
};
}
if( !isset( $parent['children'] ) )
$html .= "\n";
else
$html .= "{$t1}</ul>\n";
$html .= "{$t0}</li>\n";
return $html;
}
function getIndent( $indent, $pad = " " ){
return str_pad("", ( $indent * 4 ), $pad);
}
echo "</pre>";
我在 Apache/2.4.18 (Win32) PHP/7.0.4) 上测试过,100% 有效。
输出
数组树结构 Parent 是通过引用传递的,所以它的修改不是 returned,我们需要 return 用于 HTML
$parent = array (
0 =>
array (
'id' => 1,
'title' => 'item1',
'link' => 'http://www.example.com/item1',
'parent_id' => 0,
'children' =>
array (
0 =>
array (
'id' => 2,
'title' => 'item2',
'link' => 'http://www.example.com/item2',
'parent_id' => 1,
'children' =>
array (
0 =>
array (
'id' => 3,
'title' => 'item3',
'link' => 'http://www.example.com/item3',
'parent_id' => 2,
),
1 =>
array (
'id' => 5,
'title' => 'item5',
'link' => 'http://www.example.com/item5',
'parent_id' => 2,
'target' => NULL,
'css_class' => NULL,
),
),
),
1 =>
array (
'id' => 4,
'title' => 'item4',
'link' => 'http://www.example.com/item4',
'parent_id' => 1,
),
2 =>
array (
'id' => 6,
'title' => 'item6',
'link' => 'http://www.example.com/item6',
'parent_id' => 1,
'target' => '_blank',
),
),
),
1 =>
array (
'id' => 8,
'title' => 'item8',
'link' => 'http://www.example.com/item8',
'parent_id' => 0,
'children' =>
array (
0 =>
array (
'id' => 7,
'title' => 'item7',
'link' => 'http://www.example.com/item7',
'parent_id' => 8,
'css_class' => 'test',
),
),
),
)
HTML
<ul class="menu menu_lv0" >
<li id="menu_item_1" class="menu_item item_lv1">
<a class="menu_link" href="http://www.example.com/item1" >item1</a>
<ul class="sub_menu menu_lv1 parent_1" >
<li id="menu_item_2" class="menu_item item_lv2">
<a class="menu_link" href="http://www.example.com/item2" >item2</a>
<ul class="sub_menu menu_lv2 parent_2" >
<li id="menu_item_3" class="menu_item item_lv3">
<a class="menu_link" href="http://www.example.com/item3" >item3</a>
</li>
<li id="menu_item_5" class="menu_item item_lv3">
<a class="menu_link" href="http://www.example.com/item5" >item5</a>
</li>
</ul>
</li>
<li id="menu_item_4" class="menu_item item_lv2">
<a class="menu_link" href="http://www.example.com/item4" >item4</a>
</li>
<li id="menu_item_6" class="menu_item item_lv2">
<a class="menu_link" href="http://www.example.com/item6" target="_blank" >item6</a>
</li>
</ul>
</li>
<li id="menu_item_8" class="menu_item item_lv1">
<a class="menu_link" href="http://www.example.com/item8" >item8</a>
<ul class="sub_menu menu_lv1 parent_8" >
<li id="menu_item_7" class="menu_item item_lv2 test">
<a class="menu_link" href="http://www.example.com/item7" >item7</a>
</li>
</ul>
</li>
</ul>
我冒昧地添加了一些我认为是导航菜单通常实现的功能。
- CSS Class,如果一行包含
$row ['css_class']
,则<li>
会将其值添加到 class 中,您可以将其设置为false|null
或者只是没有。 - 目标,如果项目包含
$row ['target']
,则<a>
标签将具有目标属性及其值,您可以将其设置为false|null
或不设置。
你可以看到这些测试用例:
- 大多数项目省略
target
和css_class
我不喜欢使用class这个词,因为它是一个保留字 - item5 将
target
和css_class
设置为null
任何false
值都将忽略这些标签。 - item6 的
target
值为_blank
并且输出 中的 link - item7 有
css_class
个test
并且有这些 classesmenu_item item_lv2 test
<a target="_blank" ..
这是我添加到 HTML
的 classes 的列表UL Classes:
- 顶级
<ul>
将有 class 的menu
- 子级别
<ul>
将有 class 的sub_menu
- 子级别
<ul>
将具有parent_{id}
的 class 其中{id}
是 parent - Every
<ul>
将有一个 class ofmenu_lv{lv}
其中{lv}
是嵌套级别
UL Classes & Id:
- Every
<li>
的 ID 为menu_item_{id}
其中{id}
是记录 ID - 每个
<li>
将有 class 个menu_item
- 每个
<li>
将有一个 class 的item_lv{lv}
其中{lv}
是嵌套级别
一个Classes:
- 每个
<a>
标签将有一个 class ofmenu_link
唯一的问题是不要在记录中手动设置 children
键,$parent['children']
或 $child['children']
。我不得不用它来检测 children 是否已经被添加。 (虽然可以通过一些工作来改进,但它似乎并不重要)
这需要大量的工作和时间,因此 CSS 您只能靠自己了。但是,我会说我已经构建了很多很多菜单,这个 HTML 应该非常接近您的需要(尽管我承认,我制作导航菜单已经有几年的感觉了)。
最后你可以用这个 link
在 PHPSanbox 中在线测试它祝你好运,享受!