PHP / MySQL build tree menu

后端 未结 3 843
夕颜
夕颜 2020-11-29 03:24

I am trying to build an un-oredered list menu tree from my database in PHP and MySQL.

I have an array of page objects I am returning from the db. Each page object

相关标签:
3条回答
  • 2020-11-29 03:37

    I like @mario's solution, and have improved on it with the prevention of the excess <ul>. I would just recommend doing an ORDER BY on your SQL query to get the menu in the order you want (might even recommend a weight/sequence column be added to the schema.

    Data setup:

    $menu = array( // Presumed to have been coming from a SQL SELECT, populated for demo.
      array('id'=>1,'title'=>'Menu 1',          'parent_id'=>null),
      array('id'=>2,'title'=>'Sub 1.1',         'parent_id'=>1),
      array('id'=>3,'title'=>'Sub 1.2',         'parent_id'=>1),
      array('id'=>4,'title'=>'Sub 1.3',         'parent_id'=>1),
      array('id'=>5,'title'=>'Menu 2',          'parent_id'=>null),
      array('id'=>6,'title'=>'Sub 2.1',         'parent_id'=>5),
      array('id'=>7,'title'=>'Sub Sub 2.1.1',   'parent_id'=>6),
      array('id'=>8,'title'=>'Sub 2.2',         'parent_id'=>5),
      array('id'=>9,'title'=>'Menu 3',          'parent_id'=>null),
    );
    

    Handling:

    function has_children($rows,$id) {
      foreach ($rows as $row) {
        if ($row['parent_id'] == $id)
          return true;
      }
      return false;
    }
    function build_menu($rows,$parent=0)
    {  
      $result = "<ul>";
      foreach ($rows as $row)
      {
        if ($row['parent_id'] == $parent){
          $result.= "<li>{$row['title']}";
          if (has_children($rows,$row['id']))
            $result.= build_menu($rows,$row['id']);
          $result.= "</li>";
        }
      }
      $result.= "</ul>";
    
      return $result;
    }
    echo build_menu($menu);
    

    Output:

    <ul>
      <li>Menu 1<ul>
        <li>Sub 1.1</li>
        <li>Sub 1.2</li>
        <li>Sub 1.3</li>
      </ul></li>
      <li>Menu 2<ul>
        <li>Sub 2.1<ul>
          <li>Sub Sub 2.1.1</li>
        </ul></li>
        <li>Sub 2.2</li>
      </ul></li>
      <li>Menu 3</li>
    </ul>

    0 讨论(0)
  • 2020-11-29 03:52

    Instead of querying the database recursively, you can just pull out all entries and make the output function recursive. It's often as trivial as:

    function print_list($array, $parent=0) {
        print "<ul>";
        foreach ($array as $row) {
            if ($row->parent_id == $parent) {
                print "<li>$row->title";
                print_list($array, $row->id);  # recurse
                print "</li>";
        }   }
        print "</ul>";
    }
    

    It's only important to nest <ul>s into <li>. Or just use HTML and leave out the closing </li>.

    Actually this prints too many <ul>s, so I would check for existence of sublevels and avoid printing it directly.

    0 讨论(0)
  • 2020-11-29 03:56

    I ended up going with this solution (pastebin reference):

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

    Example Data:

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

    Usage:

    $menu = new MenuBuilder();
    echo '<pre>' . htmlentities( $menu->get_menu_html() ) . '</pŕe>';
    
    0 讨论(0)
提交回复
热议问题