Since writing Dynamic Multi-level CSS Menu with PHP and MySQL. SEO Ready I have learned quite a lot and in doing so found a much more efficient way of building this menu. This method varies in that it only makes one query to the menu table and compiles the results into a multidimensional array. The basic recurring function was just about the same, just taking into account the changes in data structure. Lets start with the query and array.
// Select all entries from the menu table
$result=mysql_query("SELECT id, label, link, parent FROM menu ORDER BY parent, sort, label");
// Create a multidimensional array to conatin a list of items and parents
$menu = array(
'items' => array(),
'parents' => array()
);
// Builds the array lists with data from the menu table
while ($items = mysql_fetch_assoc($result))
{
// Creates entry into items array with current menu item id ie. $menu['items'][1]
$menu['items'][$items['id']] = $items;
// Creates entry into parents array. Parents array contains a list of all items with children
$menu['parents'][$items['parent']][] = $items['id'];
}
The $menu contains 2 other arrays, items holds every result from the menu table query, the parents array holds a list of all item ids that have children. Next we use a while statement to run through the sql results and assign items to the arrays. If the items parent id already exists in the parents array it will be overwritten so there will only be 1 of each parent id listed.
// Menu builder function, parentId 0 is the root
function buildMenu($parent, $menu)
{
$html = "";
if (isset($menu['parents'][$parent]))
{
$html .= "
<ul>\n";
foreach ($menu['parents'][$parent] as $itemId)
{
if(!isset($menu['parents'][$itemId]))
{
$html .= "<li>\n <a href='".$menu['items'][$itemId]['link']."'>".$menu['items'][$itemId]['label']."</a>\n</li> \n";
}
if(isset($menu['parents'][$itemId]))
{
$html .= "
<li>\n <a href='".$menu['items'][$itemId]['link']."'>".$menu['items'][$itemId]['label']."</a> \n";
$html .= buildMenu($itemId, $menu);
$html .= "</li> \n";
}
}
$html .= "</ul> \n";
}
return $html;
}
echo buildMenu(0, $menu);
This version signifigantly reduces the strain on your server if you have hundreds or thousands of pages and still allows you to keep a completely dynamic menu.
Incoming search terms:
- php mysql multi level menu
- multi level accordion menu
- multi level menu in php
- select a name as parent b name as child from menus as a menus as b where a id = b parent_id
- php multi level categories
- php multi level menu database
- multi level menu
- search engine database design
- mysql multiple menu array
- multi level category php
- create multi level category php
- php accordion menu


27 Comments
Hey man, nice work there. V2 is way better than V1.
1 issue though:
the “if” in line 15 is redundant since you already did the check in line 11.
if isset returned true then all you had to do was add the code inside the if block of line 15 inside an else block instead of wasting another if check.
keep up the good work!
Ty, I will adjust this. Most likely just an oversite at the time this was written. Nowadays my experience with php in general has massively improved. Ty for your comments..
Thanks I’ve been looking for it. You saved my day cheers!
I was trying to use CSS to style this menu but still cannot figure out how to apply class to the . Can you post the full code with using class?
Oops, the sanitizer removed parts of my code!
I have modified the code to:
1. Hide content of submenus beyond top level.
2. If the file is # then the submenu opens without a link.
$menuiterations = 0; // Check how deep is the function call
// Menu builder function, parentId 0 is the root
function buildMenu($parent, $menu) {
global $menuiterations;
$html = '';
if (isset($menu['parents'][$parent])) {
if ($menuiterations<2) { // Top level menu
$html .= ''."\n";
}
else { // Deeper nested menu does not display submenus
$html .= ''."\n";
}
foreach ($menu['parents'][$parent] as $itemId) {
if ($menu['parents'][$itemId]==0) {
$html .= ''.$menu['items'][$itemId]['title_el'].$menuiterations.''."\n";
}
else {
$html .= ''.$menu['items'][$itemId]['title_el'].$menuiterations.'';
$menuiterations = $menuiterations+1; // Increase function call depth
$html .= buildMenu($itemId, $menu);
$html .= '';
}
}
$html .= ''."\n";
$menuiterations = $menuiterations-1; // Decrease function call depth
}
return $html;
}
However, I still need that the submenu is open when I load a page that belongs to the submenu. That is the display: none part should be so only when the current file is not in the current menu tree branch. I am afraid I didn’t understand your reply to Neil about the additional array.
Basically you will need a function that checks if # is a child of this menu item. Use that check before rendering each item.
Fantastic work danieliser…
I was making Sub Menus by using two functions.
1. To get all the Parent elements first.
2. To get all the childs (called in the above function with parameter as id)
But the Disadvantage was that it increased number of SQL queries.
So my Friend Lelebart suggested me this and it works very nice…
Well done
Hi danieliser,
I hope you can still help me with this awesome dynamic menu structure you have build.
I am trying to build a dynamic accordion menu which is as follows:
-Home
-Products
– Cookware
– Homeware
– Decorative
- Contact Us
Home
Products
Cookware
Homeware
Cookware
Decorative
Contact Us
I don’t know if the above makes any sense but the basic function of the menu is that if you click on “Products” it will expand and if you click on “Homeware” under “Products” it will expand as well.
My main problem is that I each parent to have its own structure so that it can be hidden and displayed when you click on its parent.
Hope you can assist me
Thank you,
Andre
Ok.. This makes several requests for a similar feature.. I can add functionality like that in the next update. If you know a little jquery and dont want to wait it can be done by setting the menu to show all children and hide them via jquery.. The only thing that is missing to make the menu work like you want is the jquery.
Hi,
Sorry I could not get the code that I inserted to show. I surrounded my code with
tags but it did not work unless I’m being blond and doing it wrong?I am actually using jquery to display and hide the different parents.
The problem I just found with the jquery example I’m using is that it only allows for a two menu accordion menu!!!!! Which will not work.
Looking forward to your update on this using jquery or some sort of accordion menu.
Thank you for replying so quickly,
Andre
@Andre, I have to appoligize, i thought your original comment was on my Vertical Menu Widget plugin for wordpress. You should be able to format the menu any way you want based on this script. You can customize this to use divs instead of li if you wanted, try to find a demo of a jquery menu that you like and look at how it needs to be structured, then imitagte that structure in your menu script.
Thx!
Hi,
After reading your tutorial, I realise that my biggest problem is the association of the given script to all the other parts.
I mean, in this case, if you want to work with a MySQL database you'll need to get the connection with an host.inc.php and than
include "host.inc.php";
mysql_connect($host,$user,$pass);
mysql_select_db($db);
However, I always wanted to link this to a vertical menu… I already have the vertical menu ), now I just need to
think a little bit, on how this could be done…
@Alex, I assumed most people would know to include db connections on their own.
As for converting it to a vertical menu that is simple.. no changes are necessary to the php script at all..
You simply need to restyle it via css.. Instead of the li using display:inline-block, you could use block and that would make each on a new line.
You might have to modify the script for your JS dropdowns depending on how your selecting them.
Hi
I've been racking my brains to come up with a way to only display and sub menus if they are a child of the level you are in. So similar to what you have shown I am setting up the query string so I know what page I'm on and have that highlighting. I then put in
if($pageID == $menu['items'][$itemID]['id']){
$html .= buildLeftMenu($itemID, $menu, 'y');
}
However if you are then on a page that is a child then the menu is not drawn.
Cheers
Neil
@Niel, sorry it took so long to reply, been very busy lately.
What you will need to do is create another function that takes the current page id and returns an array of all parents.
Then you can use
if((in_array($menu['items'][$itemID]['id',$parent_id_array)) || ($pageID == $menu['items'][$itemID]['id']))Hope this helps.
Hi,
I would like to add the level 0 items with class=”header” and it’s child with class=”nav_body”.
How can I do it?
Thanks,
bruce2046
@bruce2046, Sorry it took so long to reply. Been extremely busy.
You can add level functionality by changing
$html .= buildMenu($itemId, $menu);to
if(!isset($level)) $level = 0;$level++;
$html .= buildMenu($itemId, $menu);
$level--;
now you can easily add classes by level by change all <li> lines in the script to
<li class='level" . $level . "'>Now each li will have a level class like ‘level1′
hope this helps
OMG mate this little script is so amazing and i have now made a little menu system for phpbb3 with it Thanks
Dave
Thank you very much. If you’d like to support us link to us or to our content. Again glad to see people getting use of this.
Hi danieliser,
This is perfect – you’re the man
Thanks a lot.
R.
@Razzer
No problem. It took me quite a while to get a grasp and come up with this menu. There is more info on styling this menu on in this article: Dynamic Multi-level CSS Menu with PHP and MySQL. SEO Ready.
thanks for the feedback. if you wanna support us link to us. Thanks again
Im assuming you are using the :active pseudo class for your clicked link. And also assuming $_GET['p'] is a page id passed with the link to the next page or with the page load itself. so you could do it like this
<code>function is_active($p_id){
$active_class = '';
if (isset($_GET['p']) && $_GET['p'] == $p_id) {
$active_class = "class='active_class'";
}
return $active_class;</code>
then replace
<code>$html .= "
<a href='".$menu['items'][$itemId]['link']."' >".$menu['items'][$itemId]['label']."
";</code>
with
<code>$html .= "
<a ".is_active($itemId)." href='".$menu['items'][$itemId]['link']."'>".$menu['items'][$itemId]['label']."
";</code>
that should add the class if the item is = to the current page id. If its not then the $p will be empty
Hi there,
Very nice work
I’m trying to add a css class to the script, but I can’t make it work.
The idea is that when you click on a specific menu point, it will be highlighted by inserting a class e.g.
<a href="?p=1">Home</a>I’m using p=[id] instead of link (#home).
Do you happens to have an idea on how I can build that function into the script?
if (isset($_GET['p']) && $_GET['p'] != ”) {
$p = $_GET['p'];
} else {
$p = 1;
}
function getSelected($v) {
global $p;
if ($p == $v) {
echo ‘ class=”selected”‘;
}
}
Cheers,
Razzer
Very nice Script,
I trying to add the active class to the Script, like Danieliser have done it, but it doesn´t work.
I don´t have an Page ID, i have a Page Variable with description like this: $page = news; and my Links of this Script are named same as the $page variabel.
How do i make it work ?
if (isset($_GET["page"])) {
$page = $_GET["page"];
}
if (in_array($page, $menu['items'][$itemId]['link'])) { $activeclass="selected"; }
$html .= "".$menu['items'][$itemId]['label'].""";
How I make it work ?
Best thing you can do in this case is to try printing the variables using print_r(); Try `print_r($menu['items'][$itemId]['link']);` This allows you to debug issues.
One Trackback
[...] die Runde da ich ja neu hier im Forum bin Ich habe mir ein PHP Navigationsscript von hier geholt: http://wizardinternetsolutions.com/w…ti-level-menu/ Dieses hab ich bereits ein wenig modifiziert damit es meinen Ansprüchen zurecht kommt. Jetzt [...]