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 或不设置。

你可以看到这些测试用例:

  • 大多数项目省略targetcss_class我不喜欢使用class这个词,因为它是一个保留字
  • item5targetcss_class 设置为 null 任何 false 值都将忽略这些标签。
  • item6target 值为 _blank 并且输出
  • 中的 link <a target="_blank" ..
  • item7css_classtest 并且有这些 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 中在线测试它

祝你好运,享受!