在php中显示多级数据库驱动菜单

在php中显示多级数据库驱动菜单,php,mysql,Php,Mysql,我想显示一个数据库驱动的多级菜单,下面是我迄今为止尝试过的,但我没有得到预期的结果。有人能帮我修一下吗。我得到的输出只是父id为0的主菜单,而不是子菜单项 <?php include('system/connection.php'); ?> <?php //select all rows from the main_menu table $q = "SELECT * FROM catelogue WHERE cat_visibility = '1'"; $r = mysqli

我想显示一个数据库驱动的多级菜单,下面是我迄今为止尝试过的,但我没有得到预期的结果。有人能帮我修一下吗。我得到的输出只是父id为0的主菜单,而不是子菜单项

<?php
include('system/connection.php');
?>
<?php

//select all rows from the main_menu table
$q = "SELECT * FROM catelogue WHERE cat_visibility = '1'";
$r = mysqli_query($dbc, $q);

echo "<ul class='dropdown'>";
while ($rows = mysqli_fetch_assoc($r)) {
    if ($rows['cat_parentid'] == 0) {
        echo "<li><a href='#'>" . $rows['cat_name'] . "</a>";
        echo "<ul>";
        foreach ($rows as $item) {
            if ($item['cat_parentid'] == $rows['cat_id']) {
                echo "<li><a href='#'>" . $item['cat_name'] . "</a>";
                echo "</li>";
            }
        }
        echo "</ul>";
        echo "</li>";
    }
}
echo "</ul>";

?>
我想要的期望输出:

<ul class="dropdown">
  <li><a href='#'>Home</a></li>
  <li><a href='#'>About</a>
    <ul>
      <li><a href='#'>History</a></li>
      <li><a href='#'>Services</a></li>
    </ul>
  </li>
  <li><a href='#'>Contact</a></li>
</ul>

您所谓的
$rows
实际上是一行。然后,在
foreach($rows as$item)
循环中,它遍历此行的列。因此,没有
$item['cat\u parentid']
。使用
var\u dump()
尝试查看
$rows
$item
的输出

我想到的一个可能解决方案的初步想法是,首先迭代结果行,并将子项保存在父项中(注意:此处必须添加一些数组初始化):

然后遍历
$menuItems
数组生成输出,这将非常有用

此外,对sql结果进行排序也会很有好处,可以确保最上面的菜单项排在第一位:

"SELECT * FROM catelogue WHERE cat_visibility = '1' ORDER BY cat_parentid ASC";

您所称的
$rows
实际上是一行。然后,在
foreach($rows as$item)
循环中,它遍历此行的列。因此,没有
$item['cat\u parentid']
。使用
var\u dump()
尝试查看
$rows
$item
的输出

我想到的一个可能解决方案的初步想法是,首先迭代结果行,并将子项保存在父项中(注意:此处必须添加一些数组初始化):

然后遍历
$menuItems
数组生成输出,这将非常有用

此外,对sql结果进行排序也会很有好处,可以确保最上面的菜单项排在第一位:

"SELECT * FROM catelogue WHERE cat_visibility = '1' ORDER BY cat_parentid ASC";

您所称的
$rows
实际上是一行。然后,在
foreach($rows as$item)
循环中,它遍历此行的列。因此,没有
$item['cat\u parentid']
。使用
var\u dump()
尝试查看
$rows
$item
的输出

我想到的一个可能解决方案的初步想法是,首先迭代结果行,并将子项保存在父项中(注意:此处必须添加一些数组初始化):

然后遍历
$menuItems
数组生成输出,这将非常有用

此外,对sql结果进行排序也会很有好处,可以确保最上面的菜单项排在第一位:

"SELECT * FROM catelogue WHERE cat_visibility = '1' ORDER BY cat_parentid ASC";

您所称的
$rows
实际上是一行。然后,在
foreach($rows as$item)
循环中,它遍历此行的列。因此,没有
$item['cat\u parentid']
。使用
var\u dump()
尝试查看
$rows
$item
的输出

我想到的一个可能解决方案的初步想法是,首先迭代结果行,并将子项保存在父项中(注意:此处必须添加一些数组初始化):

然后遍历
$menuItems
数组生成输出,这将非常有用

此外,对sql结果进行排序也会很有好处,可以确保最上面的菜单项排在第一位:

"SELECT * FROM catelogue WHERE cat_visibility = '1' ORDER BY cat_parentid ASC";

这是一个递归解决方案

代码已完全注释

processMenuEntry
例程中有两个有用的检查,可以方便地执行,以便您可以决定是否要执行不同的操作

  • 检查“当前”条目是否为“根”节点

    $isRoot=$currentEntry['cat_id']==0;//进行“首次”处理
    

  • 检查当前“条目”是否有“子菜单”

    if(!empty($subMenu)){…

守则:

数据库连接:

$DB_HOST     = "localhost";
$DB_USER     = "test";
$DB_PASSWORD = "test";
$DB_TO_USE   = "testmysql";

$dbc = new mysqli($DB_HOST, $DB_USER, $DB_PASSWORD, $DB_TO_USE);
子菜单查询:

/** -----------------------------------------------------------------
 * Select all the menu entries for a given entry Id
 *
 * Note: it is safe to do 'parameter substitution' rather than using
 *       'prepared queries' with placeholders as this 'entry Id' never
 *       comes from an external source.
 *
 * @param mysqli $dbc
 * @param integer $currentEntryId
 * @return array of menu entries
 */
function selectSubMenu($currentEntryId, $dbc)
{
    $sql = "SELECT cat_id, cat_name, cat_parent_id
                   FROM catalogue
                   WHERE cat_parent_id = {$currentEntryId}
                   ORDER BY cat_id";
    $resultSet = mysqli_query($dbc, $sql);
    return $resultSet->fetch_all(MYSQLI_ASSOC);
}
处理当前菜单项:

/** --------------------------------------------------------------------------
 * Process the current menu enty - it will return a complete menu as HTML
 *
 * These needs to know whether the current entry has a submenu and
 * will therefore need to generate an 'unordered list' for the current entry.
 *
 * Alas, it also has to not display the 'list item text' for the  'root' entry
 * but process the 'submenu'.
 * This complicates the <li> current entry text generation slightly.
 *
 * @param array $currentEntry - one row
 * @param array $subMenu - many rows - may be empty
 * @param mysqli $dbc - database connection
 * @return string - the HTML for the menu
 */
function processMenuEntry($currentEntry, array $subMenu, $dbc)  {
    $outMenu = '';
    $isRoot = $currentEntry['cat_id'] == 0; // do 'First time' processing

    // display the current entry text as a 'list item'
    $outMenu .= !$isRoot ? "<li><a href='#'>" . $currentEntry['cat_name'] . "</a>" : '';

    // does it have a submenu...
    if (!empty($subMenu)) { // we need to add a complete submenu of <ul> ... </ul>

        // Start of the submenu as an unordered list -- decide what is required
        if ($isRoot) {
            $outMenu .= '<ul class="dropdown">';
        }
        else {
            $outMenu .= '<ul>';
        }

        // Display each entry of the submenu as a 'list item' 
        foreach ($subMenu as $submenuEntry) {
            $outMenu .= processMenuEntry($submenuEntry,
                                    selectSubMenu($submenuEntry['cat_id'], $dbc),
                                    $dbc);
        }

        // close the current submenu - terminate the unordered list
        $outMenu .= '</ul>';
    }

    // terminate the current list item
    $outMenu .= !$isRoot ? '</li>' : '';
    return $outMenu;
};
/**--------------------------------------------------------------------------
*处理当前菜单条目-它将以HTML形式返回完整的菜单
*
*这些需要知道当前条目是否有子菜单和
*因此,需要为当前条目生成“无序列表”。
*
*唉,它还必须不显示“根”条目的“列表项文本”
*但要处理“子菜单”。
*这使当前条目文本生成稍微复杂化。
*
*@param数组$currentEntry-一行
*@param array$子菜单-多行-可能为空
*@param mysqli$dbc-数据库连接
*@return string-菜单的HTML
*/
函数ProcessMentry($currentEntry,array$子菜单,$dbc){
$outMenu='';
$isRoot=$currentEntry['cat_id']==0;//执行“第一次”处理
//将当前条目文本显示为“列表项”
$outMenu.=!$isRoot?”
  • “:”; //它是否有子菜单。。。 如果(!empty($subMenu)){//我们需要添加一个完整的
      子菜单。
    //以无序列表的形式开始子菜单--确定所需内容 如果($isRoot){ $outMenu.='
      ; } 否则{ $outMenu.='
        '; } //将子菜单的每个条目显示为“列表项” foreach($子菜单作为$子菜单项){ $outMenu.=processMenuEntry($submenuEntry, 选择子菜单($submenuEntry['cat_id',$dbc), $dbc); } //关闭当前子菜单-终止无序列表 $outMenu.='
      '; } //终止当前列表项 $outMenu.=!$isRoot?“”:”; 返回$outMenu; };
  • 处理所有菜单项:

    /* -------------------------------------------------------------------
     * Process all the menu entries
     *
     * We need a complete menu 'tree' that includes a 'root' which is not provided
     * in the database. I think it should be. Whatever, i need one.
     *
     * Initializing a 'tree walk' i always find 'interesting' ;-/
     */
    $root = array('cat_id' => 0, 'cat_name' => '', 'cat_parent_id' => 0);
    
    // build the output menu
    $outMenu = processMenuEntry($root,
                               selectSubMenu($root['cat_id'], $dbc),
                               $dbc);
    
    // wrap it in a <div>
    $htmlMenu = '<div style="border: 1px solid red">'
                . $outMenu
                .'</div>';
    ?>
    
    /*-------------------------------------------------------------------
    *过程
    
    <!DOCTYPE html>
    <html>
      <head>    
        <title>Test Recursive Menu Builder
        </title>
      </head>
      <body>
        <div style="border: 1px solid red">
          <ul class="dropdown">
            <li>
            <a href='#'>Home</a>
            </li>
            <li>
            <a href='#'>About</a>
            <ul>
              <li>
              <a href='#'>History</a>
              </li>
              <li>
              <a href='#'>Services</a>
              </li>
            </ul>
            </li>
            <li>
            <a href='#'>Contact</a>
            </li>
          </ul>
        </div>
      </body>
    </html>