Jump to content


Photo

Multidimensional array for nested menu items


Best Answer requinix, 06 December 2017 - 05:05 PM

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. Go to the full post


  • Please log in to reply
3 replies to this topic

#1 ronnij

ronnij
  • New Members
  • Pip
  • Newbie
  • 2 posts

Posted 06 December 2017 - 03:49 PM

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.phpfr...ay#entry1544308 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, 06 December 2017 - 03:51 PM.


#2 requinix

requinix
  • Administrators
  • Maddening Administrator
  • 9,464 posts
  • LocationWA

Posted 06 December 2017 - 05:05 PM   Best Answer

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.
The Reimann Zeta Function Trolley Problem | "Summer is when I, the great ice fairy, can show my true power!"

#3 Barand

Barand
  • Moderators
  • Sen . ( ile || sei )
  • 17,907 posts

Posted 06 December 2017 - 05:44 PM

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('&mdash;', $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);

If you are still using mysql_ functions, STOP! Use mysqli_ or PDO. The longer you leave it the more you will have to rewrite.

Donations gratefully received






moon.png

|baaGrid| easy data tables - and more
|baaChart| easy line, column and pie charts

#4 ronnij

ronnij
  • New Members
  • Pip
  • Newbie
  • 2 posts

Posted 07 December 2017 - 04:37 PM

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, 07 December 2017 - 04:37 PM.





0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users