PHP - 从数据库表生成菜单

时间:2015-04-27 09:47:34

标签: php

我有来自J Bruni的代码,我在另一篇SO帖子中找到了这个代码。但是我的数据库结构有点不同,我需要让这些代码与我的表结构一起使用。

原始代码适用于此结构:

/*** SAMPLE DATABASE: ***

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

*/

但是我的表结构是这样的:

CREATE TABLE tbmenu (
  row_id      Integer(11) NOT NULL AUTO_INCREMENT,
  category_id Integer(11) NOT NULL,
  menuitem    NVarChar(128) COLLATE utf8_general_ci NOT NULL,
  pageurl     NVarChar(128) COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (
      row_id
  )
) ENGINE=InnoDB ROW_FORMAT=COMPACT DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

这是原始数据库结构的原始代码:

<?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 );
    }
}

$menu = new MenuBuilder();
echo '<pre>' . htmlentities( $menu->get_menu_html() ) . '</pre>';



?>

..这是为我的数据库结构修改的代码:

<?php

/**
 * Generate HTML for multi-dimensional menu from MySQL database
 * with ONE QUERY and WITHOUT RECURSION
 * @author J. Bruni
 */

     function is_iterable($var)
    {
      return $var !== null
        && (is_array($var)
            || $var instanceof Traversable
            || $var instanceof Iterator
            || $var instanceof IteratorAggregate
            );
    }

class MenuBuilder
{
    /**
     * MySQL connection
     */
    var $conn;

    /**
     * Menu items
     */
    var $items = array();

    /**
     * HTML contents
     */
    var $html  = array();

    /**
     * Create MySQL connection
     */
    function MenuBuilder()
    {
        $this->conn = mysqli_connect( "localhost", "rXXXt", "rXXXt" );
        mysqli_select_db( $this->conn , "XXXXdb" );
    }

    /**
     * Perform MySQL query and return all results
     */
    function fetch_assoc_all( $sql )
    {
        $result = mysqli_query( $this->conn, $sql  );

        if ( !$result )
            return false;

        $assoc_all = array();

        while( $fetch = mysqli_fetch_assoc( $result ) )
            $assoc_all[] = $fetch;

        mysqli_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 row_id, category_id, menuitem, pageurl FROM tbmenu ORDER BY row_id;";
        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();


        if (is_iterable($this->items))
        {
          foreach ( $this->items as $item )
            $children[$item['category_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']['row_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']['pageurl'],   // %2$s = link (URL)
                    $option['value']['menuitem']   // %3$s = title
                );
                $this->html[] = $tab . "\t" . '<ul class="submenu">';

                array_push( $parent_stack, $option['value']['category_id'] );
                $parent = $option['value']['row_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']['pageurl'],   // %2$s = link (URL)
                    $option['value']['menuitem']   // %3$s = title
                );
        }

        // HTML wrapper for the menu (close)
        $this->html[] = "</ul>";

        return implode( "\r\n", $this->html );
    }
}

 $menu = new MenuBuilder();
 echo "<pre>" . $menu->get_menu_html() . "</pŕe>";

?>

Category_ID是1,2,3,4 ..每个类别都有子菜单项。类别名称在另一个表中。因此,当显示时,它将显示为具有每个类别的树,然后显示在下面的子菜单项。

我知道这个问题听起来像是“为我做功课”,但我花了2个小时试图修改代码使其工作,但无济于事。我对PHP的数组和相关语法有点新,所以我觉得很难。

提前致谢!

2 个答案:

答案 0 :(得分:1)

如果我没有错,原始代码中的parent_id指向同一个表的id,在你的情况下,它是某个其他表的id。为了使用相同的代码,您必须修改表的结构。

答案 1 :(得分:0)

function topMenu() {
        global $MySQLi;
        $query = "SELECT * FROM `grp_menuitems` WHERE `hidden` = '0' AND `parent` = '0' ORDER BY `order`";
        $commit = $MySQLi->query($query);
        if($commit === false) {
            header("Location: error?code=01SET");
            die();
        }
        else
        {
            while($row = $commit->fetch_assoc()) {
                if($_SERVER['PHP_SELF'] == $row['link']) {
                    echo '<li class="current_page_item"><a href="'.$row['link'].'">'.$row['title'].'</a>';
                }
                else
                {
                    echo '<li><a href="'.$row['link'].'">'.$row['title'].'</a>';
                }
                $this->topMenuChildren($row['id']);
                echo '</li>';
            }
        }
    }

    function topMenuChildren($parent) {
        global $MySQLi;
        $query = "SELECT * FROM `grp_menuitems` WHERE `hidden` = '0' AND `parent` = '".$parent."' ORDER BY `order`";
        $commit = $MySQLi->query($query);
        if($commit === false) {
            header("Location: error?code=01SET");
            die();
        }
        else
        {
            if($commit->num_rows > 0) {
                echo '<ul>';
                while($row = $commit->fetch_assoc()) {
                    echo '<li><a href="'.$row['link'].'">'.$row['title'].'</a>';
                    $this->topMenuChildren($row['id']);
                    echo '</li>';
                }
                echo '</ul>';
            }
        }
    }

这是我的功能。但是,我使用order而不是position作为列。根据需要进行调整。 :)

只需致电topMenu();,其余部分通过PHP完成。我知道它与您的代码结构有点不同,但您似乎拥有能够分析并获得想法的知识。