1

i m trying to build recursive menu using PHP but not succeeding

mysql table

menuid name parentid

and my php code

function generateMenubar()
{
    $data = Yii::app()->db->createCommand("select * from menu");

    $result = $data->queryAll();        

    $html = '<ul class = "navigation">';

    foreach($result as $row)
    {
        if($row['parentid'] == "0")
        {
            $html .= '<li><a href="#"><span>'.$row["menuname"].'</span></a>';

            $menu_id = $row['menuid'];

            $html .= $this->generateHTML($result,$menu_id,$html);
        }
    }       

    return $html;
}

function generateHTML($result,$menu_id,$html)
{       
    foreach($result as $row_sub)
    {
        if($menu_id == $row_sub['parentid'])
        {
            $html .= '<ul><li><a href="buttons.html"><span>'.$row_sub['menuname'].'</span></a>';

            $menu_id = $row_sub['menuid'];
            $html .= $this->generateHTML($result,$menu_id,$html);
            $html .= '</li>';
        }           
    }
    return $html.'</ui>';
}

but this loop is not stopping and generating wrong output. it can have sub levels upto n level. i want to make it dynamic cuz levels may change in future any suggestion ?

Chintan_chiku
  • 425
  • 2
  • 12
  • 24
  • there is such a thing as "nested sets". It is a kind of mathematical concept to have n-dimensinal nesting of elements. Yii has a behaviour for this. It might help/improve your solution: http://www.yiiframework.com/extension/nestedsetbehavior/ – Andresch Serj Mar 14 '14 at 10:39
  • i wrote an edit to my solution. Still i hope you switch to nested sets. – Andresch Serj Mar 14 '14 at 11:18
  • I think my Answer was more than helpfull. I basically wrote the code for you. Consider marking it as accepted. – Andresch Serj Mar 27 '14 at 10:46

1 Answers1

0

Your problem is that you do not have a structured result to iterate over. You might have SubElement 2 > 3 > 4 as your first result but 2 > 3 > 1 as your 5th Result. So you can't just iterate over the result once and build your html.

What you want to do (appart from, switching to nested sets, which is what you REALLY want to do wink) is structure your result first.

Iterate over your result and build a nested array to iterate over recusively to build your HTML. To find "where to put your element" in your recursive array you need to recursively check back with your existing array always as well. IF all you store is id and parent id, how to find out what the root element is before you checked ALL elements right? So i could write the code to do so, but i rather do not because it would be a horrible solution anyway. To do so more efficiently it would really help if you do not only store your parentid but a level as well. Then you could store your elements in a two dimensional array storing all elements for each level and then recursively use that array. i.e.

$navigationTempArray = array();
foreach($fakeMySQLResult as $row)
{
    if(!array_key_exists($row['level'], $navigationTempArray )) {
        $navigationTempArray[$row['level']] = array();
    }
    if(!array_key_exists($row['parentid'], $navigationTempArray[$row['level']] )) {
        $navigationTempArray[$row['level']][$row['parentid']] = array();
    }
    $navigationTempArray[$row['level']][$row['parentid']][] = $row;
}

now you have an array like this:

array (
  0 => array(
    'root' => array(
      1 => array('title' => 'Start' ...)
      2 => array('title' => 'Team' ...)
      3 => array('title' => 'Projects' ...)
    )
  ),
  1 => array(
    2 => array(
      4 => array('title' => 'Development' ...)
      5 => array('title' => 'Design' ...)
      6 => array('title' => 'Sales' ...)
    ),
    3 => array(
      7 => array('title' => 'Mayhem' ...)
      8 => array('title' => 'X' ...)
    )
  ),
  2 => array(
    4 => array(
      9 => array('title' => 'PHP' ...)
     10 => array('title' => 'MySQL' ...)
    )
  )
)

Now you can iterate over this array recursively, solving every level for every item to infinity ;-)

function returnSubNavigation($id,$level,$fullNavigationArray) {
$html = '';
if(array_key_exists($level, $fullNavigationArray) && array_key_exists($id, $fullNavigationArray[$level])) {
    $html .= '<ul>';
    foreach($fullNavigationArray[$level][$id] as $subElement) {
        $html .=  '<li><a href="#"><span>'.$subElement["menuname"].'</span></a>';
        $html .=  returnSubNavigation($subElement['id'], $level+1, $fullNavigationArray);
        $html .=  '</li>';
    }
    $html .= '</ul>';

}
return $html;

}

echo returnSubNavigation('root', 0, $navigationTempArray);

Here is an online fiddle kind of thing that proves it works

Some people who do not want to use nested sets often store pathes rather than parent id's. i.e.:

| id   | path |
|    1 |    1 |
|    2 |    2 |
|    3 |  1.1 |
|    4 |  1.2 |
|    5 |  2.1 |
|    6 |    3 |

This is a lot easier (cheaper in terms of performance) to iterate over. You can sort it a lot easier. Still, it brings a lot of problems and restrictions.

Community
  • 1
  • 1
Andresch Serj
  • 35,217
  • 15
  • 59
  • 101