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
Hi, I enjoyed your tutorial. However with a category structure of up to 3000 different rows. The code does run slow.
Posted on 2009-02-12 04:30:44Hy Barry, By "you need to select the rows inside the function instead of declaring the menu_array as a global" he means to remove global declaration of array from the function definition and pass it to the function as a argument.
Posted on 2009-07-01 05:58:12Hi, thanks so much for this code; I am writing a file manager component for Joomla and I couldn't figure out how to manage unlimited sub-categories cleanly, and with only using one database query.
Posted on 2009-07-13 01:59:25Great codes, I am really having a hard time displaying the cat subcat that I have in a way it will show up like a combo box. Thank you!
Posted on 2009-11-20 23:58:48You have just solved my 2 day-old vb headache with that recursive function. Awesome job
Posted on 2010-03-03 06:46:23GReat piece of code. Tested with a very very large category table and things are little slow. However for my shop that has 4 levels of categories this is absolutely perfect.
Posted on 2010-03-08 13:13:08Thanks
Is it possible, using this code, to output a categories and it's subCat based on a specific ID?
Posted on 2010-03-25 11:11:28Web Developement ID is 1. I want to fetch only Web Developement and it's child.
How do I do that?
Please help
Hi Thanks for sharing program.but how can i display all parent & child categories in dropdown with differentiation..
Posted on 2010-09-03 05:51:01like
Parent1<br>
---Childparent1<br>
Parent2<br>
---Child1parent2<br>
------childchild1parent2
Hi,
Posted on 2010-12-18 02:45:46really nice code but I want a multi dimension array one row for one record. but I can't able to do this I am new in php can you help me. actually I want to show this data in a list format.
regards,
Mahi,
Hi,
Posted on 2011-01-10 06:15:34this worked perfectly. can you tell me how to worked this with combobox?(dropdown)
Thanks for any reply.
very good example.i just want to add just two lines.
Posted on 2011-06-30 19:53:461. add id field also
$menu_array[$row['id']] = array('name' => $row['name'],'parent' => $row['parent'],'link' => $row['link'],'id'=>$row['id']);
2. make it dynamic
echo '<li><a href="products.php?cid='. $value['id'] . '">' . $value['name'] . '</a>';
thanks
Works perfectly...
Posted on 2011-09-14 06:22:32one single query ... print all the categories and its subcategories(multiple level)..
But if its a huge category list i wud rather cache the output in a file and fetch it from there,
so that i dont have to use this function
unless there was a change in the (CRUD operation done on the category table) categories list.
thanks
Arif
Hi
Posted on 2011-12-23 03:19:04Thanks Codeassembly for such an easy code It works for Asp .net C# Tooo
Thanks
For ASP .net guys here is my code:
Bind dataset or genric list with data and follow the code.
string treeList = string.Empty;
public string BindEmp(int ParentId)
{
Boolean hasChild = false;
foreach (DataRow dr in ds.Tables[0].Rows)
{
if (ParentId == Convert.ToInt32(dr["projEmp_ParentEmpId"]))
{
if (hasChild == false)
{
hasChild = true;
treeList += "<ul>";
}
treeList += "<li>" + dr["empname"].ToString();
BindEmp(Convert.ToInt32(dr["projEmp_EmpId"]));
treeList += "</li>";
}
}
if (hasChild == true)
{
treeList += "</ul>";
}
return treeList;
}
Hope this help someone.
Thanks
Anuj Koundal
Hi
Posted on 2011-12-26 05:53:58Need your some more help. This code works fine if one category is inserted only once. But how do we achieve if we insert category more than once.
Like
WebDevelopment
HTML
MISC
HTML
HTML1
HTML2
HTML3
Other
HTML
here HTML lies three times.
If you have some time please help.
Very nice indeed. But I'd prefer not to use globals, so this is my adaptation:
Posted on 2012-02-28 12:48:56<code>
function generate_menu($parent,$menu_array=null) {
$has_childs = false;
foreach($menu_array as $key => $value) {
if ($value['parent'] == $parent) {
if ($has_childs === false) {
$has_childs = true;
echo "<ul>\n";
}
echo '<li><a href="/category/' . $value['name'] . '/">' . $value['name'] . '</a>';
generate_menu($key,$menu_array);
echo "</li>\n";
}
}
if ($has_childs === true) echo "</ul>\n";
}
</code>
to everyone having trouble alter your query from
Posted on 2013-03-06 05:00:13$query = mysqli_query($link,'SELECT * FROM categories');
to
$query = mysqli_query($link,'SELECT * FROM categories ORDER BY parent');
Hi
Posted on 2013-02-15 01:42:10Need your some more help. This code works fine if one category display category and subcategory as menu. but i want to display product after click on product... plz help me...
Make yourself heard