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

时间:2015-04-28 04:22:37

标签: php mysql

我想显示一个数据库驱动的Multilevel菜单,下面是我到目前为止所尝试的,但我没有得到所需的结果。任何人都可以帮我解决这个问题。我得到的输出只是父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>";

?>

我的数据库结构

-----------------------------------------
| cat_id   | cat_name    | cat_parentid |
-----------------------------------------
|  1       |  Home       |  0           |
|  2       |  About      |  0           |
|  3       |  Contact    |  0           |
|  4       |  History    |  2           |
|  5       |  Services   |  2           |
-----------------------------------------

我想要的输出:

<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>

2 个答案:

答案 0 :(得分:2)

这是递归解决方案。

代码已完全注释。

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

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

    $isRoot = $currentEntry['cat_id'] == 0; // do 'First time' processing

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

    if (!empty($subMenu)) { ...

Q29910284-display-multilevel-database-driven-menu.php

代码:

数据库连接:

$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;
};

处理所有菜单条目:

/* -------------------------------------------------------------------
 * 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>';
?>

输出生成的HTML:

<!DOCTYPE html>
<html>
<head>
    <title>Test Recursive Menu Builder</title>
</head>
<body>
<?= $htmlMenu ?>
</body>
</html>

生成的HTML

<!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>

答案 1 :(得分:1)

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

我想到的一个可能的解决方案草案是首先遍历结果行并将子项保存在父项中(注意:这里必须添加一些数组初始化):

while ($row = mysqli_fetch_assoc($r)) {
    $menuItems[$row['cat_id']] = $row;

    $parentId = $row['cat_parentid'];
    $menuItems[$parentId]['sub_items'][] = $row['cat_id'];
}

然后遍历$menuItems数组生成输出,recursion对此非常有用。

此外,订购sql结果将是有益的,以确保首先是顶级菜单项:

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