How to create a nested menu from MySQL with PHP? - php

I need to create a menu with PHP from a MySQL database.
Table called categories has id, name, parent_id, shortdesc, etc.
The output need to have parent list and children list under the partent list as follows.
If you can show me codes or website, I will appreciate it.
<ul id="catmenu">
<li class="menulist">Cars
<ul>
<li>Ford</li>
<li>Honda</li>
<li>Toyota</li>
</ul>
</li>
<li class="menulist">Food
<ul>
<li>Pasta</li>
<li>Pizza</li>
...
</ul>
</li>
...
...
</ul>

This is specifically for two levels deep. Recommended approach should it be more is to use an optimized table structure for traversal, like http://articles.sitepoint.com/article/hierarchical-data-database/2 (pointed out elsewhere) or to pull the data you need and push it into a dictionary (associative array) and query it that way.
<?php
$query = <<<EOT
SELECT
parent.name as parent_name,
child.name as child_name,
FROM
items child
INNER JOIN
items parent
ON
child.parent_id = parent.id
ORDER BY
parent.name
EOT;
$result = mysql_query($query) or die('Failure!');
echo "<ul id=\"catmenu\">";
$last_parent = '';
while($row = mysql_fetch_array($result)){
// If this is a new category, start a new one
if($last_parent != $row['parent_name']){
// Unless this is the first item, close the last category
if($last_parent != ''){
echo "</ul></li>";
}
$last_parent = $row['parent_name'];
echo "<li class=\"menulist\">{$row['parent_name']}<ul>";
}
echo "<li>{$row['child_name']}</li>";
}
// If we actually had items, close the "category"
if($last_parent != ''){
echo "</ul></li>";
}
echo "</ul>";
?>

If you have only two levels then, you could just display them :
echo '<ul id="catmenu">';
foreach($menu as $element) {
echo '<li><ul class="menulist">';
foreach($element['submenu'] as $submenu) {
echo '<li>' . $submenu['name'] . '</li>';
}
echo '</ul></li>';
}
echo '</ul>
If you have an undefined number of submenus however you should use a Recursive Function.
function menu($item) {
$ret = '<li>' . $item['name'];
if (!empty($item['submenu'])) {
foreach($item['submenu'] as $submenu) {
$ret .= menu($submenu);
}
}
return $ret;
}
echo menu($menu);
So every of your subcategories, whatever their number is will be displayed.

You make database like this.
ID NAME PARENT
0 Cars -1
1 Foods -1
2 Ford 0
3 Honda 0
4 Toyota 0
5 Pasta 1
6 Pizza 1
...
You query them all up and put it in an array.
$Menus = array();
// In a read MySQL loop
$Menus[$i]['ID']
$Menus[$i]['NAME']
$Menus[$i]['PARENT']
// Sorry, lazy to write. I think you know what I mean.
Then you loop all menu looking for PARENT == -1. Generate all UL and IL then sub it with another nested menu.
You can simply create a function like this.
var $MenuLevelClass = array("menulist");
function CreateMenu($Menus, $Tab = 0, $Parent = -1, $Level = 0) {
global $MenuLevelClass;
$CatClass = ($Level != 0) ? '' : ' class="catmenu"';
$MenuClass = $MenuLevelClass[$Level];
if ($MenuClass != '')
$MenuClass = ' class="'.$MenuClass.'"';
$TabCount = $Level + $Tab;
$TabUL = "";
for ($t = 0; $t < $TabCount; $t++)
$TabUL = $TabUL."\t";
$TabLI = $TabUL."\t";
?>
<?=$TabUL?><ul<?=$CatClass?>>
<?php
$MenuCount = count($Menus);
for ($m = 0; $m < $MenuCount; $m++) {
$Menu = $Menu[$m];
$ID = $Menu['ID'];
if ($ID != $Parent)
continue;
?>
<?=$TabLI?><li<?=$MenuClass?>><?=$Menu['Name']?><?=CreateMenu($Menus, $Tab + 1, $ID, $Level + 1)?></li>
<?php
?>
<?=$TabUL?></ul>
<?php
}
}
And to use it just run 'CreateMenu($Menus);' or 'CreateMenu($Menus, $PrefixTabCount);'.
CreateMenu will recursively create the nested menu for you.
I have not test it so you may have to adjust it.
Hope this helps.

Related

How to create a list view from a database table?

I'm querying a category table id, parent_id, name to generate list view of all the categories and sub-categories. What I have so far is:
HP
Laptops
PC
Sports
Sunglasses
Food
House
The function that I use to generate the output is:
public function get_category($parent_id = NULL)
{
$query = $this->db->get_where('category', array('parent_id' => $parent_id))->result();
$result = '';
foreach ($query as $row)
{
$i = 0;
if ($i == 0)
{
$result .= '<ul>';
}
$result .= '<li><a href="'.base_url().'category/edit/'.$row->id.'">' . $row->name;
$result .= $this->get_category($row->id);
$result .= '</li>';
$i++;
if ($i > 0)
{
$result .= '</ul>';
}
}
return $result;
}
This is what I want to achieve:
HP
HP > Laptops
HP > PC
Sports
Sports > Sunglasses
Food
House
Try following
public function get_category($parent_id = NULL,$level, $prev = "")
{
$query = $this->db->get_where('category', array('parent_id' => $parent_id))->result();
$result = '';
foreach ($query as $row)
{
$temp = $prev;
if($level == 0){
$temp .= '' . $row->name.'';
}else{
$temp .= ' > ' . $row->name.'';
}
$result .= $temp;
$result .= "<br />";
$result .= $this->get_category($row->id, $level + 1, $temp);
}
return $result;
}
First run of this function should be
get_category(NULL, 0, "");
Here
NULL is parent id (you can use a loop to dynamicaly pass id)
level should be 0
prev should be empty
I've created a demo according to your needs and it worked for me. I'm sharing the code below, I've done explaining in the comments wherever necessary. See if it helps you.
Controller
public function get_category($parent_id = NULL){
// get the {category}, {sub_category} from the table, if {sub_category} stored in different table use {JOIN}
$data['category'] = $this->db->get_where('category', array('parent_id' => $parent_id))->result();
$this->load->view('your-view', $data);
}
View
<?php
if(!empty($category)){ // check if {$category} has value; if not, do nothing
$catArr = array(); // initialize array to store category id
?>
<ul> <!-- Start ul -->
<?php
foreach($category as $cat){ //loop through all the {category} array
// check if the {cat_id} already exists in array; if not, do so where {cat_id} is category id(change your value here)
if( ! in_array( $cat->cat_id, $catArr ) ){
$catArr[] = $cat->event_id;
?>
<li><?php echo $cat->cat_name; ?></li> <!-- show {cat_name} in list -- your category name here -->
<?php
}
?>
<!-- Show {cat_name} and {subcat_name} both -- (your values here). Give links or whatever here(your styling)-->
<li><?php echo $cat->cat_name; ?> > <?php echo $cat->subcat_name; ?></li>
<?php
}
?>
</ul>
<?php
}
?>
OUTPUT
Honda
Honda > Car
Honda > Bike
Philips
Philips > Electricals
Philips > Electronics

How Would I Turn Mysqli Table Results into a menu with drop down

Here Is my question: What I am wanting To do is Take Results from a mysql table and turn them into a menu and a drop down menu
HERE IS A QUICK EXAMPLE:
if you see in my mysql table i have page_name and parent, So the example is:
page_name and if i have row 1 the page_name is 'Home' now it's parent is 'none' right but on id number 39 the page_name is 'Contact Us' and the Parent Is 'Far Far Away 123' so if the parent is equal to 'none' then it will show at the top of the menu not the drop down if it has a parent it will show under that parent like this:
Home | the ben page | The Brock Page | Far Far Away 123 | dsfk
Contact Us
You see Contact Us is under Far Far Away Because the parent Is Far Far Away 123
here is my table:
Here is my code That I am trying but it is not working for some reason:
<ul>
<?php
$sql = "SELECT * FROM pages ORDER by item_order";
$result = mysqli_query($db, $sql);
confirm_query($result);
while ($links = mysqli_fetch_assoc($result)) {
if($links['parent'] !== "none") {
?>
<li id = "<?php echo $links['id']; ?>"><a href="
<?php
echo "page.php?id=" . $links['id'] . "\" title=\"" . $links['page_title'] . "\"";
?>>
<?php
echo $links['page_name'];
?>
</a>
<?php
if($links['parent'] !== "none") {
$child = "";
$sql = "SELECT * FROM pages";
$result = mysqli_query($db, $sql);
while($row = mysqli_fetch_assoc($result)) {
if($row['parent'] !== "none") {
$child = $row['page_name'];
}
}
echo "<ul id=\"sub_menu\" class=\"sub_navagation" . $links['id'] . "\">";
echo "<li>";
echo $child;
echo "<li>";
echo "</ul>";
}
?>
</li>
<?php
}
}
?>
</ul>
CSS:
#sub_menu {
display: none;
}
#sub_menu:hover {
display: block;
}
Ok if as you can see i have the parent row in the MYSQL table and on id number 39 i want the 'Far Far Away123' to be the parent of Contact Us and i want to show it when i hover over 'Far Far Away123'
My suggestion is to build out an array of all the results. Then run through that array (instead of multiple database queries).
I added a function build_dropdown() that will take the page name and run through the array of pages to see if there are any items with a parent matching. If so, we make an array of those items and run through them to build the dropdown menu. If not, it does nothing and moves on to the next menu item.
<?php
function build_dropdown ($parent, $pages){
foreach($pages as $page){
if($page['parent'] == $parent){
$items = $page;
} // END if
} // END foreach
if(is_array($items)){ // If a sub
echo '<ul id="sub_menu" class="sub_navagation'. $item['id'] .'">';
foreach($items as $item){
echo '<li>'.$item['name'].'<li>';
} // END foreach
echo '</ul>';
} // END if
}
$sql = "SELECT * FROM pages ORDER by item_order";
$result = mysqli_query($db, $sql);
confirm_query($result);
while ($row = mysqli_fetch_assoc($result)) {
$pages[] = $row; // Add each row to $pages array to use later
}
foreach($pages as $key => $page){
if($page['parent'] == 'none'){ ?>
<li id = "<?php echo $page['id']; ?>">
<a href="page.php?id=<?php echo $page['id']; ?>" title="<?php echo $page['page_title']; ?>">
<?php echo $page['page_name']; ?>
</a>
<?php
build_dropdown($page['page_name'], $pages); // If there are child items then build them out
?>
</li>
<?php
} // END if
} // END foreach
?>
I suggest you will need to JOIN your table to basically query it again to get the parent value, and add that to your markup.
SELECT *
FROM Pages
LEFT JOIN Pages p2 on page_name = p2.parent
(note: the syntax above may not be right, but I wanted to give you an idea of where I would start).

PHP Database driven Multilevel Menu

I want to built a database driven multilevel menu I got it from [http://abhijitpal.in/][1] but the problem here is i can't put class = "dropdown" only to the top menus <ul>, it also get applies to the sub menus as well. I just modified a little but I don't know how to use recursive function. Below is the code, please see if you can help
<?php /** Function to display Catelogue Menu */
//select all rows from the main_menu table
$q ="SELECT * FROM catelogue WHERE cat_visibility = '1'";
$r = mysqli_query($dbc, $q);
//create a multidimensional array to hold a list of menu and parent menu
$menu = array(
'menus' => array(),
'parent_menus' => array()
);
//build the array lists with data from the menu table
while ($row = mysqli_fetch_assoc($r)) {
//creates entry into menus array with current menu id ie. $menus['menus'][1]
$menu['menus'][$row['cat_id']] = $row;
//creates entry into parent_menus array. parent_menus array contains a list of all menus with children
$menu['parent_menus'][$row['cat_parentid']][] = $row['cat_id'];
}
// Create the main function to build milti-level menu. It is a recursive function.
function nav_catelogue($parent, $menu) {
//$html = "";
if (isset($menu['parent_menus'][$parent])) { ?>
<ul>
<?php
foreach ($menu['parent_menus'][$parent] as $menu_id) {
if (!isset($menu['parent_menus'][$menu_id])) { ?>
<li><?php echo $menu['menus'][$menu_id]['cat_name']; ?></li>
<?php }
if (isset($menu['parent_menus'][$menu_id])) { ?>
<li><?php echo $menu['menus'][$menu_id]['cat_name']; ?>
<?php echo nav_catelogue($menu_id, $menu); ?>
</li>
<?php }
} ?>
</ul>
<?php }
}
?>
My Database structure is
-----------------------------------------
| cat_id | cat_name | cat_parentid |
-----------------------------------------
| 1 | Home | 0 |
| 2 | About | 0 |
| 3 | Contact | 0 |
| 4 | History | 2 |
| 5 | Services | 2 |
-----------------------------------------
Desired Output I want:
HOME
ABOUT
History
Services
Contact
This is the final code as per #Mave
<?php /** Function to display Catelogue Menu */
//select all rows from the main_menu table
$q ="SELECT * FROM catelogue WHERE cat_visibility = '1'";
$r = mysqli_query($dbc, $q);
//create a multidimensional array to hold a list of menu and parent menu
$menu = array(
'menus' => array(),
'parent_menus' => array()
);
//build the array lists with data from the menu table
while ($row = mysqli_fetch_assoc($r)) {
//creates entry into menus array with current menu id ie. $menus['menus'][1]
$menu['menus'][$row['cat_id']] = $row;
//creates entry into parent_menus array. parent_menus array contains a list of all menus with children
$menu['parent_menus'][$row['cat_parentid']][] = $row['cat_id'];
}
// Create the main function to build milti-level menu. It is a recursive function.
function nav_catelogue($parent, $menu, $top = false) {
if (isset($menu['parent_menus'][$parent])) {
//this is short code for if($top === true) { //do true } else { //do false }
echo $top ? '<ul class="dropdown">' : '<ul>';
foreach ($menu['parent_menus'][$parent] as $menu_id) {
if (!isset($menu['parent_menus'][$menu_id])) {
echo '<li>' . $menu['menus'][$menu_id]['cat_name'] . '</li>';
}
if (isset($menu['parent_menus'][$menu_id])) {
echo '<li>' . $menu['menus'][$menu_id]['cat_name'] . '' . nav_catelogue($menu_id, $menu) . '</li>';
}
}
echo '</ul>';
}
}
?>
function nav_catelogue($parent, $menu, $top = false) {
if (isset($menu['parent_menus'][$parent])) {
//this is short code for if($top === true) { //do true } else { //do false }
echo $top ? '<ul class="dropdown">' : '<ul>';
foreach ($menu['parent_menus'][$parent] as $menu_id) {
if (!isset($menu['parent_menus'][$menu_id])) {
echo '<li>' . $menu['menus'][$menu_id]['cat_name'] . '</li>';
}
if (isset($menu['parent_menus'][$menu_id])) {
echo '<li>' . $menu['menus'][$menu_id]['cat_name'] . '' . nav_catelogue($menu_id, $menu) . '</li>';
}
}
echo '</ul>';
}
}
When you first call nav_catelogue (not present in your current code), call it with nav_catelogue($menu_id, $menu, true);
You have two ways to make your recursive function put the
class="dropdown"
The first way is to go for a calling/callee style in which your calling function will treat the first case(class="dropdown") and then call the recursive function which will handle the general case(no class="dropdown").
Add an argument(boolean ?) that you'll transmit at first call and which will add the class="dropdown". On the recursive calls you'll transmit this boolean as false(no class="dropdown" insertion).

Hierarchical tree menu - PHP / MySQL

In working on a dynamic menu w/ CRUD I have been having some trouble with the finishing touches. It works, but there are some extra tags getting inserted where there shouldn't be and can't figure out how to clean it up before I share it with the world. I used this as a starting point, then changed it to work with an accordion menu (http://www.phpro.org/tutorials/Simple-Mysql-PHP-Menu.html).
Below is the data from the table (I changed the names on the first 2 fields to get it to fit in the SO format from menu_item_id to id, and from menu_parent_id to pid).
id pid menu_item_name menu_url sortorder status
1 0 Settings 0 ACTIVE
2 5 Grid Demo grid.php ACTIVE
3 5 setGridOptions gridoptions.php ACTIVE
4 1 Menu Items adminmenu.php 1 ACTIVE
5 0 Grid Settings 100 ACTIVE
6 1 General Settings settings.php 100 ACTIVE
Here is the PHP that connects to the mysql database and creates the hierarchical tree array to make it work:
include 'db.php';
$sql = "SELECT * FROM menu_items WHERE status = \"ACTIVE\" ORDER BY sortorder, menu_item_name";
$query = $db->query($sql);
while($data = $query->fetch(PDO::FETCH_ASSOC))
// loop over the results
{
// Assign by reference
$thisref = &$refs[ $data['menu_item_id'] ];
// add the the menu parent
$thisref['menu_item_id'] = $data['menu_item_id'];
$thisref['menu_parent_id'] = $data['menu_parent_id'];
$thisref['menu_item_name'] = $data['menu_item_name'];
$thisref['menu_url'] = $data['menu_url'];
// if there is no parent id
if ($data['menu_parent_id'] == 0)
{
$list[ $data['menu_item_id'] ] = &$thisref;
}
else
{
$refs[ $data['menu_parent_id'] ]['children'][ $data['menu_item_id'] ] = &$thisref;
}
}
function create_list( $arr )
{
$html = "";
foreach ($arr as $key=>$v)
{
if ($v['menu_parent_id'] == '0')
{
$html .= '<a class="menuitem submenuheader" href="'. $v['menu_url'] .'">'.$v['menu_item_name']."</a>\n";
$html .= "<div class=\"submenu\">\n<ul>\n";
$html .= "<li>" . create_list($v['children']) . "</li>";
$html .= "</ul>\n";
}
else{
$html .= '<li><a id="' . $v['menu_item_id'] . '">'.$v['menu_item_name']."</a></li>\n";
}
}
$html .= "</div>\n";
return $html;
}
echo "<div class=\"glossymenu\">";
echo create_list( $list );
echo "</div>";
When I run it, it outputs the following:
<div class="glossymenu"><a class="menuitem submenuheader">Settings</a>
<div class="submenu">
<ul>
<li><li><a id="4">Menu Items</a></li>
<li><a id="6">General Settings</a></li>
</div>
</li></ul>
<a class="menuitem submenuheader" href="">Grid Settings</a>
<div class="submenu">
<ul>
<li><li><a id="2">Grid Demo</a></li>
<li><a id="3">setGridOptions</a></li>
</div>
</li></ul>
</div>
</div>
As you can see there are extra <li> tags, the </ul> is in the wrong
spot (should be after the </div>) Other than that, it is working great.
The other thing I can't figure out is if I have a root menu item with no children, I would love it to have a different output like
<a id="8">No Children Menu Item</a>
Instead of:
<a class="menuitem submenuheader">No Children Menu Item</a>
The second example would create the make it show up the little (+/-) for expanding and contracting and wouldn't allow me to click on it. For clarification on the <a> tags, I am using javascript to do a .get() based off the id which is why there is no href or url shown.
UPDATE
It is working correctly and I posted it on Github for anyone that wants it.
https://github.com/ajhalls/php-accordian-menu
Try this :
<?php
include 'db.php';
$sql = "SELECT * FROM menu_items WHERE status = 'ACTIVE' ORDER BY pid ASC, sortorder ASC, menu_item_name ASC";
$query = $db->query($sql);
$menu_items = array();
while($data = $query->fetch(PDO::FETCH_ASSOC)) {
if($data['pid'] == 0) {
$menu_items[$data['id']] = array();
$menu_items[$data['id']]['id'] = $data['id'];
$menu_items[$data['id']]['name'] = $data['menu_item_name'];
$menu_items[$data['id']]['url'] = $data['menu_url'];
$menu_items[$data['id']]['children'] = array();
} else if($data['pid'] != 0) {
$tmp = array();
$tmp['id'] = $data['id'];
$tmp['name'] = $data['menu_item_name'];
$tmp['url'] = $data['menu_url'];
array_push($menu_items[$data['pid']]['children'],$tmp);
unset($tmp);
}
}
function create_list($arr)
{
$html = "";
foreach($arr as $key => $value) {
if(count($value['children']) > 0) {
$html .= ' <a class="menuitem submenuheader" href="'. $value['url'] .'">'.$value['name'].'</a>
<div class="submenu">
<ul>';
foreach($value['children'] AS $child) {
$html .= ' <li>
<a id="'.$child['id'].'">'.$child['name'].'</a>
</li>';
}
$html .= ' </ul>
</div>';
} else{
$html .= ' <a id="'.$value['id'].'">'.$value['name'].'</a>';
}
}
return $html;
}
echo "<div class=\"glossymenu\">";
echo create_list($menu_items);
echo "</div>";
?>

php mysql vertical menu

I am trying to design a dynamic vertical menu :
my table is:
primary // primary key
pid //parent id 0 if none
cid //the actual category id
comment //some comment
the thing is that I want to design a php function where after reading the values from database it should output it into an html ordered list (like a multilevel nested unordered list)
I know that it would be easily achieved by using a recursion function
the problem is that i just can't do it.. I've tried many times but failed in vain
The main problem comes in nesting (where to give list items and where to start the list)
I would be very grateful if anyone of you could help me out...
well i've managed to write an ugly code:
{here i ve used two tables one for the parent and one for the child}
$query="SELECT * FROM parentCat";
$result=mysql_query($query);
echo "<ul id=\"suckertree1\">";
while($row=mysql_fetch_array($result))
{
$name=$row['comment'];
$pid=$row['catid'];
echo "<li> $name ";
$query="select * from childCat WHERE pid=$pid";
$subresult=mysql_query($query);
$af=mysql_num_rows($subresult);
if($af>0)
{
echo "<ul>";
while($subrow=mysql_fetch_array($subresult))
{
$name=$subrow['comment'];
echo "<li> $name </li>";
}
echo "</ul>";
}
echo "</li>";
}
echo "</ul>";
it will show only one sublevel...
wht should i do to make it work for infinite level
I thing a while script is best for you
$query = mysql_query("SELECT * FROM menu ORDER BY primary ASC");
$parent=0;
$sub=0
echo "<ul>";//start list
while($menu = mysql_fetch_array($query){
if($parent != $menu['pid']){//if not seen item before
if($sub != 0){echo "</ul>";}else{$sub++;}//if not first submenu, close submenu before. If first sub sub++.
echo "<ul>";}//open submenu
echo "<li>".$menu[cid]."</li>";//echo item
if($parent != $menu['pid']){//if not seen before
$parent = $menu['pid']; //set to seen before so next loop will be recognised
}
}
echo "</ul>"; //end list
I dont know if this is gonna work since I did not test it, but it should show you an option on how it could be done. Idea of lists:
<ul>
<li>Item1</li>
<ul>
<li>Subitem1</li>
<li>Subitem2</li>
</ul>
<li>Item 2</li>
<ul>
<li>Subitem1 of Item2</li>
</ul>
</ul>
Gives:
Item1
Subitem1
Subitem2
Item2
Subitem1 of Item2
Try this, it should do the trick.
<?php
$query = "SELECT a.comment parent
, b.comment child
FROM menu a
JOIN menu b
ON a.primary = b.pid
ORDER BY a.primary";
$result = mysql_query($query);
$parent = '';
echo "<ul>";
foreach ($result as $next) {
if ($next['parent'] != $parent) {
if (strlen($parent) > 0) {
echo " </ul>";
echo " </li>";
}
echo " <li>" . $next['parent'];
echo " <ul>";
}
echo " <li>" . $next['child'] . "</li>";
$parent = $next['parent'];
}
echo " </ul>";
echo " </li>";
echo "</ul>";
?>
To render a nested list for an assoc array try this:
<?php
$list = array(
'item-1' => 'test-1',
'item-2' => 'test-2',
'item-3' => array(
'item-3-1' => 'test-3',
'item-3-2' => array(
'item-3-2-1' => 'test-4',
'item-3-2-2' => 'test-5',
),
),
'item-4' => 'test-6',
);
function render_list($list) {
echo '<ul>';
foreach ($list as $key => $value) {
echo '<li>';
echo $key.':';
if (is_array($value)) render_list($value);
else echo $value;
echo '</li>';
}
echo '</ul>';
}
render_list($list);
Which will result in this:
<ul>
<li>item-1:test-1</li>
<li>item-2:test-2</li>
<li>
item-3:
<ul>
<li>item-3-1:test-3</li>
<li>
item-3-2:
<ul>
<li>item-3-2-1:test-4</li>
<li>item-3-2-2:test-5</li>
</ul>
</li>
</ul>
</li>
<li>item-4:test-6</li>
</ul>
item-1:test-1
item-2:test-2
item-3:
item-3-1:test-3
item-3-2:
item-3-2-1:test-4
item-3-2-2:test-5
item-4:test-6

Categories