Jump to content

Multidimensional array for nested menu items


ronnij

Recommended Posts

Hi,

 

I am having problems getting a multi level menu working and have read a lot of previous forum posts trying to make sense of it and an example like https://forums.phpfreaks.com/topic/303449-php-recursive-multidimensional-array-to-html-nested-code/?hl=%2Bmultidimensional+%2Barray&do=findComment&comment=1544308 should probably tell me everything I need to know, but I simply cannot get my head around this correctly and make it work. I hope there are some experienced coders here who will bare with my n00bness hehe.. 

 

My menu items are stored like:

mysql> SELECT id, name, parent FROM menu_items;
+----+-------------------+--------+
| id | name              | parent |
+----+-------------------+--------+
|  1 | Programming       |      0 |
|  2 | PHP               |      1 |
|  3 | Python            |      1 |
|  4 | Operating-Systems |      0 |
|  5 | Windows 2012 R2   |      4 |
|  6 | Linux Mint        |      4 |
|  7 | Network           |      0 |
|  8 | Some stuff        |      6 |
+----+-------------------+--------+


Programming
 |- PHP
 |- Python
Operating-Systems
 |- Windows 2012 R2
 |- Linux Mint
   |- Some Stuff
Network

Today it only supports 1 sub-level because I just put one loop within another like this:

 

$query = "SELECT id, name FROM menu_items WHERE parent = 0";
$result = $link->query($query);

if ($result->num_rows > 0) {
  while ($row = $result->fetch_assoc()) {
    $children = getMenuChildrenCount($row['id']);
    $links_count = getLinksCountFromParentId($row['id']);
    echo "\t\t\t\t\t\t<li class=\"haschildren\"><div><a href=\"links.php?pid={$row['id']}\" class=\"link\">".$row['name']." ({$links_count})</a> <a href=\"#\" class=\"expand\">".$children."<i class=\"fa icon\"></i></a></div>\n";
    echo "\t\t\t\t\t\t<ul>\n";
    ${query . $row['id']} = "SELECT id, name FROM menu_items WHERE parent = " . $row['id'];
    ${result . $row['id']} = $link->query(${query . $row['id']});
    if (${result . $row['id']}->num_rows > 0) {
      while (${row . $row['id']} = ${result . $row['id']}->fetch_assoc()) {
        $links_count = getLinksCountFromParentId(${row . $row['id']}['id']);
        echo "\t\t\t\t\t\t\t<li><div><a href=\"links.php?pid=".${row . $row['id']}['id']."\" class=\"link\">".${row . $row['id']}['name']." ({$links_count})</a></div></li>\n";
      }
    }
    echo "\t\t\t\t\t</ul>\n";
    echo "\t\t\t\t\t</li>\n";
  }
}

That, of course, is not optimal or the right way to do it.. Can someone please tell me how to come around multi level with X depth?

 

Thank you very much :-)

 
Link to comment
Share on other sites

This sort of parent/child thing can be handled in one of two ways:

1. Recursively. You write a function that displays the menu for a particular parent (or 0). It calls itself recursively on each child in case it has its own children.

2. Linearly with a stack. It's basically recursive but instead of function calls you track everything in an array.

 

#1 is definitely easier.

 

Can you write that function? Take a look at the code in the other thread for an example. It should look something like

function whateverYouWantToCallThisThing($link, $parent) {
	run a query for the children of $parent

	if there are children {
		output a <ul>

		for each child in the result {
			output the opening <li> and link

			whateverYouWantTocallThisThing($link, child)

			output the closing </li>
		}

		output a </ul>
	}
}
The output will look like

<ul><li>Programming<ul>
  <li>PHP</li>
  <li>Python</li></ul></li>
<li>Operating-Systems<ul>
  <li>Windows 2012 R2</li>
  <li>Linux Mint</li>
  <li>Some Stuff</li></ul></li>
<li>Network</li></ul>
Yes, you can format it more nicely than that - if you want to put in a bit more effort.
Link to comment
Share on other sites

read the records and store n an array, indexed by parent

$sql = "SELECT id
             , item
             , parent
        FROM mitem";
        
$res = $db->query($sql);
$menu = [];
foreach ($res as $item) {
    $menu[$item['parent']][$item['id']] = $item['item'];
}
Then use a recursive function to output the items and levels

function output_item(&$menu, $parent, $level=0)
{
    if (!isset($menu[$parent]) ) return;
    
    foreach ($menu[$parent] as $id =>$item) {
        $indent  = str_repeat('—', $level * 5);
        echo "$indent$item<br>";
        output_item($menu, $id, $level+1);    // re-call the function for subitems
    }
}

// output the menu
output_item($menu, 0);
Link to comment
Share on other sites

Thank you very much both of you!

 

Especially your explanatory "code" @requinix, it did the trick for me. So much headache and yet so simple... *sigh* .. 5 minutes after I read it I ended up with this which does exactly what I wanted  :happy-04:

 

function showNestedMenu($parent_id) {
  require 'config.php';
  $query = "SELECT id, name, parent FROM menu_items WHERE parent = {$parent_id}";
  $result = $link->query($query);
  if ($result->num_rows > 0) {
    echo "<ul>\n";
    while ($row = $result->fetch_assoc()) {
      $children = getMenuChildrenCount($row['id']);
      $links_count = getLinksCountFromParentId($row['id']);
      if ($children > 0) {
        echo "\t\t\t\t\t\t\t<li class=\"haschildren\"><div><a href=\"links.php?pid={$row['id']}\" class=\"link\">".$row['name']." ({$links_count})</a> <a href=\"#\" class=\"expand\">".$children."<i class=\"fa icon\"></i></a></div>\n";
        showNestedMenu($row['id']);
        echo "\t\t\t\t\t\t</li>\n";
      }
      else {
        echo "\t\t\t\t\t\t\t<li><div><a href=\"links.php?pid=".$row['id']."\" class=\"link\">".$row['name']." ({$links_count})</a></div></li>\n";
      }
    }
    echo "\t\t\t\t\t</ul>\n";
  }
}

showNestedMenu(0);

 

 

@Barand: I might wanna try your method as well, just to get the understanding :-)

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.