Page 1 of 1

building dynamic menu problem ??

Posted: Wed Nov 28, 2007 11:27 pm
by PHPycho
Hello forums!!
I am getting bit problem in displaying dynamic css menus which uses <li><ul> tags.
I had the table with structure as:
tbl_cat
|id|title|parent_id|
|1|Item 1| 0 |
|2|Item 2| 0 |
|3|Item 3| 0 |
|4|Item 4| 0 |
|5|Item Item 2.1| 2 |
|6|Item Item 2.2| 2 |
|7|Item Item 3.1| 3 |
|8|Item Item 3.2| 3 |
|9|Item Item 3.3| 3 |
I have to show these values as following in order to display the proper multilevel menu.

Code: Select all

<li><a href="#">Item 1</a></li>
    <li><a href="#">Item 2</a>
      <ul>
        <li><a href="#">Item 2.1</a></li>
        <li><a href="#">Item 2.2</a></li>
      </ul>
    </li>
    <li><a href="#">Item 3</a>
      <ul>
        <li><a href="#">Item 3.1</a></li>
        <li><a href="#">Item 3.2</a>
          <ul>
            <li><a href="#">Item 3.2.1</a></li>
            <li><a href="#">Item 3.2.2</a></li>
            <li><a href="#">Item 3.2.3</a></li>          
          </ul>
        </li>
      </ul>
    </li>
    <li><a href="#">Item 4</a></li>

Has anybody some idea in generating proper <li><ul> tags for the fields so that it becomes as above ..
Note it can be n level deep.
Thanks in advance for any tips and suggestions.

Posted: Thu Nov 29, 2007 12:03 am
by Chris Corbyn
I'll post an unoptimized version (running multiple queries for each parent_id).

Code: Select all

/* Roughly speaking we want to map item id's to titles and children:

 array(
  1 => array(
    'title' => 'Item One',
    'children' => array(
      3 => array('title' => 'Sub Item One', 'children' => array())
    )
  ),
  2 => array('title' => 'Item 2', 'children' => array())
 )
 */

/**
 * Recursively fetch branches in the menu starting with $parent_id.
 * @param int $parent_id
 * @return array
 */
function get_menu_array($parent_id = 0)
{
    $array = array();
    $sql = 'SELECT * FROM menu WHERE parent_id = ' . ((int) $parent_id);
    $result = mysql_query($sql) or die(mysql_error());
    while ($row = mysql_fetch_assoc($result))
    {
      $array[$row['id']] = array('title' => $row['title'], 'children' => get_menu_array($row['parent_id']));
    }
    return $array;
}

/**
 * Build a recursive tree menu using the given array.
 * @param array $array
 * @return string
 */
function menu_to_string($array)
{
  $ret = '<ul>';
  foreach ($array as $details)
  {
    $ret .= '<li>' . $details['title'];
    if (!empty($details['children']))
    {
      //Recurse
      $ret .= menu_to_string($details['children']);
    }
    $ret .= '</li>';
  }
  $ret .= '</ul>';
  
  return $ret;
}

//Display it
echo menu_to_string(get_menu_array());
It's possible to do without the recursion but it's possibly not worth it if there's no signifcant slowdown. It's a lot harder to write and a lot harder to read without the recursion though.

Posted: Thu Nov 29, 2007 1:04 am
by PHPycho
Thanks for the reply Mr. Chris Corbyn.
I modified your code

Code: Select all

$array[$row['id']] = array('title' => $row['title'], 'children' => get_menu_array($row['parent_id']));
to

Code: Select all

$array[$row['id']] = array('title' => $row['title'], 'children' => get_menu_array($row['id']));
the the html was obtained as:

Code: Select all

<ul>
  <li><a href="">Item 1</a></li>
  <li><a href="">Item 2</a>
    <ul>
      <li><a href="">Item 2.1</a></li>
      <li><a href="">Item 2.2</a></li>
    </ul>
  </li>
  <li><a href="">Item 3</a>
    <ul>
      <li><a href="">Item 3.1</a></li>
      <li><a href="">Item 3.2</a></li>
      <li><a href="">Item 3.3</a></li>
    </ul>
  </li>
  <li><a href="">Item 4</a></li>
</ul>
but it should be as per above html...no <ul> tags ar first...I think if from the returned value if we ommitted first <ul> and last </ul> tag..it would work fine..
Thanks for the help again..

Posted: Thu Nov 29, 2007 10:08 am
by feyd
This seems to beg for preorder trees.

Search for time we've talked about hiearchical data.