Build a simple infinite depth category system for your site, subcategories expand using javascript without refreshing the page.
Let's start by showing the database structure and data
CREATE TABLE `categories` ( `id` int(11) NOT NULL auto_increment, `name` varchar(100) NOT NULL, `parent` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=13 ; -- -- Dumping data for table `categories` -- INSERT INTO `categories` (`id`, `name`, `parent`) VALUES (1, 'Web development', 0), (2, 'Application development', 0), (3, 'Linux', 0), (4, 'Misc', 0), (5, 'Php', 1), (6, 'Mysql', 1), (7, 'Javascript', 1), (8, 'CSS', 1), (9, 'C plus plus', 2), (10, 'wxWidgets', 2), (11, 'Tutorials', 3), (12, 'My thoughts', 4);
We are using a single table to hold together categories and subcategories. Each subcategory can have a parent to show which category it belongs to, and they can be linked recursively
In this example, we have only 1 level subcategories, but you can add how many levels you would like to. For example category 'Web development' has 4 subcategories: Php, Mysql, Javascript and CSS
Using data from this table we will generate a nested html unordered list using the php code below
<?php
//connect to database
$link = mysqli_connect('localhost','root','');
mysqli_select_db($link,'your_database_name');
//get all rows
$query = mysqli_query($link,'SELECT * FROM categories');
while ( $row = mysqli_fetch_assoc($query) )
{
$menu_array[$row['id']] = array('name' => $row['name'],'parent' => $row['parent']);
}
//recursive function that prints categories as a nested html unorderd list
function generate_menu($parent)
{
$has_childs = false;
//this prevents printing 'ul' if we don't have subcategories for this category
global $menu_array;
//use global array variable instead of a local variable to lower stack memory requierment
foreach($menu_array as $key => $value)
{
if ($value['parent'] == $parent)
{
//if this is the first child print '<ul>'
if ($has_childs === false)
{
//don't print '<ul>' multiple times
$has_childs = true;
echo '<ul>';
}
echo '<li><a href="/category/' . $value['name'] . '/">' . $value['name'] . '</a>';
generate_menu($key);
//call function again to generate nested list for subcategories belonging to this category
echo '</li>';
}
}
if ($has_childs === true) echo '</ul>';
}
//generate menu starting with parent categories (that have a 0 parent)
generate_menu(0);
And the resulting html unordered list looks like this
<ul id="categories"> <li>Web development <ul> <li><a href="/category/Php/">Php</a></li> <li><a href="/category/Mysql/">Mysql</a> </li><li><a href="/category/Javascript/">Javascript</a></li> <li><a href="/category/CSS/">CSS</a></li> </ul> </li> <li>Application development <ul> <li><a href="/category/C-plus-plus/">C plus plus</a></li ><li><a href="/category/wxWidgets/">wxWidgets</a></li> </ul> </li> <li>Linux <ul> <li><a href="/category/Tutorials/">Tutorials</a></li> </ul> </li> <li>Misc <ul> <li><a href="/category/My-thoughts/">My thoughts</a></li> </ul> </li> </ul>
Now there are to ways of displaying your category menu. One is by using only css like this
#categories
{
list-style-position:inside;
list-style-image: url(/examples/categories/bullet.png);
font-size:10px;
font-family:Tahoma,Verdana,Arial;
margin:0px;
padding:0px;
}
And the other is by using javascript to make your categories expandable. This is useful if you have lots of subcategories
<ul id="categories"> <li>Web development <ul> <li><a href="/category/Php/">Php</a></li> <li><a href="/category/Mysql/">Mysql</a> </li><li><a href="/category/Javascript/">Javascript</a></li> <li><a href="/category/CSS/">CSS</a></li> </ul> </li> <li>Application development <ul> <li><a href="/category/C-plus-plus/">C plus plus</a></li ><li><a href="/category/wxWidgets/">wxWidgets</a></li> </ul> </li> <li>Linux <ul> <li><a href="/category/Tutorials/">Tutorials</a></li> </ul> </li> <li>Misc <ul> <li><a href="/category/My-thoughts/">My thoughts</a></li> </ul> </li> </ul> <script>menu_initiate();</script>
Download all files for this example, including javascript from here
Icons from sweetie.sublink.ca, javascript code (slightly improved) from javascript.internet.com
Update:Rik Moncur sent me a menu implementation based on this code, you can download the code from here dynamicmenu.zip, thanks Rik
Share this with the world
Related
Comments
Thank you very much for your article. It helped me a lot!
Posted on 2007-11-19 08:53:07The css version doesn't seem to function as per your example i.e. the first item in the menu has href link but should be just text?
Posted on 2007-10-07 08:25:25Hope that makes sense - great tutorial though :-)
Regards
Jason
thanks for the clear way you have put this tut together.
Posted on 2007-11-12 07:31:30just the facts none of the crap.
I like how you mainly focused on what we really want to know, the php
Hi,
Posted on 2008-03-21 14:02:43I have tried using the above on two different hosts - both are running php v5.
On one host, the menu displays perfectly but on the other it only ever shows the first 'parent' and first 'child'.
Is there a specific PHP Module that needs to be enabled for this menu to work? (hosts question not mine :-))
Many thanks
Kind Regards
Jason
Hi Jason, I had complains about this problem from other users, there is no php module requirement, I didn't discover the cause that is producing this problem.
Posted on 2007-11-26 07:32:29I just retested my script and it works fine, I'm using php 5.2.4 with apache on linux for my tests, but I think it works fine on older versions to.
Hi,
Posted on 2007-11-26 07:32:56Strange - I've also tried example above from Rik Moncur and it does exactly the same, works on one host but not another.
Regards
Jason
Hi, the problem is that you need to select the rows inside the function instead of declaring the menu_array as a global. It should work after that.
Posted on 2007-11-26 07:31:52Cheers Mauro - you are a star. It now works a treat :-))
Posted on 2007-11-26 07:50:36Hi Mauro, I have the same problem as Jason but I dont understand you explination. What does this mean "you need to select the rows inside the function instead of declaring the menu_array as a global."? What does that mean? Which part of the script must I edit? Can you please give us an example.
Posted on 2008-03-21 14:02:43This code is great. I'm using the one with CSS formatting, not the javascript expandable one. My problem is the category names are linked and I don't want them to be. I only want the subcategories to be linked. On this page, the example shows the categories as unlinked, but when I use the code, it makes my category names linked. Any ideas?
Posted on 2008-03-21 14:02:43What about deleting? No one ever shows about deleting. I'm not talking DELETE WHERE id = $id AND parent = $id I'm talking if theres a Parent with a Child(1) that has a child(2) that has a child(3). If you delete the main parent how would it go about deleting all the way up to child 3 as child 2 is it's parent and child 1 is child 2's parent and parent is the parent of child 1. Hope I didn't confuse anyone.
Posted on 2008-05-01 14:21:24First - thanks for the tutorial. Mauro, I had the same problem (worked on one server but only first row shows on a different server). Can you explain what you mean by selecting the rows inside the function instead of declaring the menu_array as global? Thanks.
Posted on 2008-04-18 00:57:45Hi, I tried using a $var just replace "echo" in this function but can't. Help me because I am using Xtemplate for my project.
Posted on 2008-05-01 14:22:23Thanks
Please provide an example of what you mean by "you need to select the rows inside the function instead of declaring the menu_array as a global." I have had the same issue with only one set of results being displayed.
Posted on 2008-05-01 14:22:23Thank you.
Hi, I tried using a $var just replace "echo" in this function but can't. Help me because I am using Xtemplate for my project.
Posted on 2008-05-01 14:22:23Thanks
Hi, Good Worked. I can\'t able to list the display in ul li order. with the help of your code, i can.
Posted on 2008-07-06 03:46:39Thanks
Make yourself heard