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).
Related
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
I have a tables in MySQL one is test table columns like this
id refid name userdefined
1 0 A
2 0 B
3 0 C
4 1 A1 abc
5 1 A2 cde
6 2 B1
7 2 B2
8 3 C3
9 3 c4
10 3 c5
11 2 C7
12 2 C8 lmn
13 11 c9
14 11 c10
Using the above table I am creating the dynamic menu using a PHP function.
I have one more table, it has the login fields and data like this:
id username password field3
1 john john 1,3,4,5,6,7,8,9
What i want is if John is logged in, how do I only show the menu items in field3.
I am new to PHP. I am showing all menu items using a function in
PHP please help me, thanks in advance.
<?php
//this is php function for creating menu
$sql2 = mysqli_query($conn, 'select * from login');
$row = mysqli_fetch_array($sql2);
$menu_items = explode(',', $row['field3']);
function submenu($parentid = 0)
{
global $conn;
$sql = mysqli_query($conn, "SELECT * FROM test WHERE refid=" . $parentid . " AND id IN " . ($menu_items));
{
$rowcount = mysqli_num_rows($sql);
if ($rowcount > 0) {
echo '<ul>';
}
while ($row = mysqli_fetch_array($sql, MYSQLI_ASSOC)) {
if ($row['refid'] == 0) {
echo '<li class="limain">' . $row['name'];
submenu($row['id']);
echo '</li>';
} else {
if ($row['userdefined']) {
echo '<li class="lichild">' . $row['name'] . '';
} else {
echo '<li class="lichild">' . $row['name'];
}
submenu($row['id']);
echo '</li>';
}
}
if ($rowcount > 0) {
echo '</ul>';
}
}
}
?>
$sql2=mysqli_query($conn,'select * from login');
$row = mysqli_fetch_array($sql2);
//storing the field values in menuitems variables
$menu_items = $row['field3'];
//pass the menuitem variables to query
function submenu($parentid=0){
global $conn;
$sql=mysqli_query($conn,"SELECT * FROM test WHERE refid=".$parentid ." AND
id in ($menu_items)");
{
$rowcount=mysqli_num_rows($sql);
if($rowcount>0){
echo '<ul>';
}
while($row=mysqli_fetch_array($sql,MYSQLI_ASSOC))
{
if($row['refid']==0)
{
echo '<li class="limain">'.$row['name'];
submenu($row['id']);
echo '</li>';
}
else{
if($row['userdefined']){
echo '<li class="lichild">'.$row['name'].'';
}else{
echo '<li class="lichild">'.$row['name'];
}
submenu($row['id']);
echo '</li>';
}
}
if($rowcount>0){
echo '</ul>';
}
}
}
Your function is mostly correct in principle, I had a bit of trouble following your function so I changed a few things to improve readability, but this should work.
The only issue I found that would prevent this from working was your if($refid), which is checking for any data not == to null.
0 == null //true 0 === null //false
If I understood your table correctly, entries with a refid of 0 are parent items. In that case they're the only menu items passing into that part of the function, so I changed it from if ($refid) to if ($refid === 0)
With regards to you wanting to you only wanting to show specific menu items, you'll need to run a separate query outside of the function to get the list you made. And then add a check in you submenu() function.
I didn't have a way to test this, but it should work.
//Query to get list of menu items
$menu_items = explode(',', $row['menus']);
function submenu($parentid=0, $menu_items=null) {
global $conn;
$sql=mysqli_query($conn,"SELECT * FROM test WHERE refid=".$parentid);
$rowcount=mysqli_num_rows($sql);
if($rowcount > 0) {
echo '<ul>';
}
while($row=mysqli_fetch_array($sql,MYSQLI_ASSOC)) {
if($menu_items !== null || !in_array($id, $menu_items)) {
continue;
}
$id = $row['id'];
$refid = $row['refid'];
$userdefined = $row['userdefined'];
$name = $row['name'];
if($refid === 0) {
echo "<li class='limain'>{$name}";
submenu($id, $menu_items);
echo '</li>';
} else if ($refid > 0) {
if($userdefined) {
echo "<li class='lichild'><a href='{$userdefined}'>{$name}</a>";
} else {
echo "<li class='lichild'>{$name}";
}
submenu($id, $menu_items);
echo '</li>';
}
}
if($rowcount > 0) {
echo '</ul>';
}
}
You must protect the php files too, not only the menu items. If the user know the php filename, can access with a direct link to protected area.
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).
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>";
?>
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.