Jump to content

Multidimensional array for nested menu items


Go to solution Solved by requinix,

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

 
Edited by ronnij
  • Solution

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.
  • Like 1

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);
  • Like 2

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

Edited by ronnij
This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • 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.