I am trying to create multi-level dynamic PHP menu by fetching values from database using recursion. But the page speed is getting slower and slower as the no. of records in database increases. Currently there are only around 15 records in DB.
Here is the code:
<div id="nav">
<?php
$menu_html='';
function render_menu($parent_id){
$con=mysqli_connect("localhost","root","","test");
global $menu_html;
$result=mysqli_query($con, "select * from menu where parent_id=$parent_id");
if(mysqli_num_rows($result)==0) return;
if($parent_id==0)
$menu_html .= "<ul>";
else
$menu_html .= "<ul>";
while($row=mysqli_fetch_array($result)){
$menu_html .= "<li>{$row['label']}";
render_menu($row['id']);
$menu_html .= "</li>";
}
$menu_html .= "</ul>";
return $menu_html;
}
echo render_menu(0);
?>
</ul>
</div>
This is how my db looks like:
Please help and let me know how I can optimize the page speed.
Thanks
You're doing multiple database queries which can be avoided.
Fetch everything in one go and loop over the rows to build the menu. You'll have everything you need to build a nested array using the parent IDs.
If it's still slow after that you can look into caching the database result, or even the entire rendered menu.
You probably are better off caching it in a file or to memory and specifically removing the cached item when you edit a menu item or add a new one to the database, so that the cache can be updated. Then you need not worry too much about how long you're caching for, you can just cache it indefinitely.
I would like to recommend you to learn nested set tree: Managing Hierarchical Data in MySQL, that enables only 1 select/query for building your menu, without recursive function calls.
Try this:
<div id="nav">
<?php
$menu_html='';
function render_menu()
{
global $menu_html;
$con=mysqli_connect("localhost","root","","test");
$result=mysqli_query($con, "SELECT * FROM menu ORDER BY parent_id ASC;");
$menu = array();
while($row=mysqli_fetch_array($result))
{
if($row['parent_id'] == 0)
{
$menu[$row['id']] = array
(
'label' => $row['label'],
'link' => $row['link'],
'children' => array()
);
}
else
{
$menu[$row['parent_id']]['children'][] = array
(
'label' => $row['label'],
'link' => $row['link']
);
}
}
$menu_html = '<ul>';
foreach($menu as $menu_item)
{
$menu_html .= '<li>';
$menu_html .= ''.$menu_item['label'].'';
if((bool)$menu_item['children'])
{
$menu_html .= '<ul>';
foreach($menu_item['children'] as $child)
{
$menu_html .= '<li>';
$menu_html .= ''.$child['label'].'';
$menu_html .= '</li>';
}
$menu_html .= '</ul>';
}
$menu_html .= '</li>';
}
$menu_html .= '</ul>';
return $menu_html;
}
echo render_menu();
?>
</div>
Thanks to all for your guidance and showing me the correct path to proceed.
I followed this link and its done.
http://wizardinternetsolutions.com/articles/web-programming/dynamic-multilevel-css-menu-php-mysql
Thanks
Related
I am trying to create a Nested Checkbox List in PHP. For this I have written Below Code with Multiple Nested foreach loop inside while
<?php
$sql_concern="SELECT * FROM sister_concern where Base_user='$User_id' And Status='Active'";
$result_concern = mysqli_query($link,$sql_concern);
if(mysqli_num_rows($result_concern) > 0){
echo '<ul>';
while($row = mysqli_fetch_array($result_concern)){
$sisid=$row['Id'];
$name=$row['Name'];
$namearray[]=$name;
foreach($namearray as $data){
echo '<li style="list-style-type:none;"><input type="checkbox" name="sister_concern" class="chkMainConcern" id="chkMainConcern'.$sisid.'" value="'.$sisid.'" > '.$name.' </li>';
//fetch module Name for each concern
$sql_module="SELECT * FROM Module where Status='Active'";
$result_module = mysqli_query($link,$sql_module);
//this one coming duplicate
echo '<ul class="modulename'.$sisid.'" style="display:none;">';
while($rowmodule = mysqli_fetch_array($result_module)){
$modid=$rowmodule['Id'];
$modname=$rowmodule['Name'];
$modearray[]=$modname;
foreach($modearray as $val){
//module name
echo '<li style="list-style-type:none;"><input type="checkbox" value="'.$modid.'"> '.$rowmodule['Name'].' </li>';
//fetch module permission for each module
$sql_modhooks="SELECT * FROM Module_hooks where Status='Active' and ModuleID='$modid'";
$result_modhooks = mysqli_query($link,$sql_modhooks);
echo '<ul class="modulehooks'.$modid.'" style="display:none;">';
while($row_modhooks = mysqli_fetch_array($result_modhooks)){
echo '<li style="list-style-type:none;"><input type="checkbox" value="'.$modid.'"> '.$row_modhooks['display_txt'].' </li>';
}
echo '</ul>';
}
}
echo '</ul>';
}
}
echo '</ul>';
}else{
echo 'No Concern Added Yet! Pls Add a Concer First';
}
?>
It is giving me Output like this https://imgur.com/a/xlTMB0l, Result is coming as expected. Only problem value from first foreach loop duplicating as growing. what's wrong in the code
$namearray[]=$name;
This appends to the existing array. Do unset($namearray) before this statement and it should remove your duplicates I feel.
I'm trying to build this function that display a menu with sub menus from a database. Something that can show like sub-sub-sub-menus and so on... If that's ever something I need.
In the database i have 2 tables, menu and menuItem.
menu
menuItem
php file
function get_menu($id) {
require(dirname(__FILE__) . '/config.php');
// main base
$menu = " SELECT id, menu_name, menu_data, menu_order
FROM mos_menuItem
WHERE menu_parent_id='$id'
AND menu_sub='0'
ORDER BY menu_order";
$menuResult = mysqli_query($conn, $menu);
echo '<ul>';
while ($menuRow = mysqli_fetch_row($menuResult)) {
// main menu
echo '<li>'. $menuRow[1] .'</li>';
// sub base
$sub = "SELECT id, menu_name, menu_data, menu_order, menu_sub
FROM mos_menuItem
WHERE menu_parent_id='$menuRow[0]'
AND menu_sub>='1'
ORDER BY menu_order";
$subResult = mysqli_query($conn, $sub);
echo '<ul>';
while ($subRow = mysqli_fetch_row($subResult)) {
echo '<li>'. $subRow[1] .'</li>';
}
echo '</ul>';
}
echo '</ul>';
mysqli_close($conn);
}
so I need to loop something inside itself? I'm not sure where to go from here.
Recursion is your best friend here.
I am not sure what purpose is served by the menu_sub field. I assume it tells you at what level it is in the menu hierarchy. If that is the case, then you can safely ignore it (menu_parent_id would suffice) to create this multi-level menu hierarchy.
function getMenus($menu_id, $is_sub=false){
$sql = "SELECT id, menu_name, menu_data, menu_order
FROM mos_menuItem
WHERE menu_parent_id='$menu_id'
ORDER BY menu_order";
$mResult = mysqli_query($conn, $sub);
echo '<ul>';
while ($mRow = mysqli_fetch_row($mResult)) {
echo '<li>'. $mRow[1] .'';
getMenus($mRow[0], true);
echo '</li>';
}
echo '</ul>';
}
This does have one drawback where if you want the hierarchy to start from a parent other than 0 (I assume your root menu_id is 0), that particular menu would not show up in the hierarchy.
So after some trial and error i figure it out. It really helped to look at Sheikh Azad code.
The only difference is in my function i have AND menu_sub='$sub' to make it not going loop the main menu item if the id is the same.. If that make sense.
The function
function mos_get_menu_editor($menuId, $sub) {
require(dirname(__FILE__) . '/config.php');
$item = " SELECT id, menu_name, menu_data, menu_order, menu_sub
FROM mos_menuItem
WHERE menu_parent_id='$menuId'
AND menu_sub='$sub'
ORDER BY menu_order";
$itemResult = mysqli_query($conn, $item);
echo '<ul>';
while ($itemRow = mysqli_fetch_row($itemResult)) {
echo '<li>'. $itemRow[1] .'</li>';
mos_get_menu_editor($itemRow[0], 1);
}
echo '</ul>';
mysqli_close($conn);
}
I'm trying to take the values of an array and display it as a list. I've used a foreach to do this before but for some reason I can't figure out why it's not working this time around. In the code below, $output is the contents of an entire page generated through AJAX and is later echo'd out on the bottom of the script. Everything seems to be working except this one small section. I managed to get this exact same information to display on a separate static page (without the $output) so I'm not sure why it's not working here.
if(!empty($record['utilities'])) {
$output .= "<ul>";
foreach ($record['utilities'] as $eachUtility):
// $output .= "<li>" echo $eachUtility; "</li>";
$output .= "<li>" . $eachUtility . "</li>";
endforeach;
$output .= "</ul>";
// $output .= $record['utilities']; This works
}
Here's a working example of the same code that was hard coded on a separate page:
<?php if(!empty($record['utilities'])) { ?>
<ul>
<?php foreach ($record['utilities'] as $eachUtility): ?>
<?php echo ('<li>' . $eachUtility . '</li>'); ?>
<?php endforeach ?>
</ul>
<?php } ?>
Try this:
$output = "<ul>";
foreach ($record['utilities'] as $eachUtility) {
$output .= "<li>" . $eachUtility . "</li>";
}
$output .= "</ul>";
This is a very newbie question. But how would I sort the results of this alphabetically so the names in the li list are in order?
<?php
// FOR THE CATEGORIES (PRODUCT TYPE)
$terms_one = get_terms('product_cat');
$count_one = count($terms_one);
$i_one = 0;
echo '<ul class="submenu one filter option-set" data-filter-group="gem-type">';
echo '<li>ALL';
if ($count_one > 0) {
foreach ($terms_one as $term_one)
{
$i_one++;
$term_list_one .= '<li> <span>'. $term_one->count .'</span>'. $term_one->name .'</li>';
if ($count_one != $i_one)
{
$term_list_one .= '';
} else {
$term_list_one .= '';
}
}
echo $term_list_one;
}
echo '</ul>';
?>
You can put in an array with arguments in the get_terms() function, orderby and order are probably what you want.
Here's the documentation:
http://codex.wordpress.org/Function_Reference/get_terms
$terms_one = get_terms('product_cat','orderby=FIELD&order=ASC');
More information, you can refer to the documentation here: http://codex.wordpress.org/Function_Reference/get_terms
Try
asort($terms_one);
before doing the foreach loop.
If you want to sort by the array-key you can use ksort() ...
I have this issue where i want to increase a value with 1 and apply this to my HTML, but i can't use a for() or while() loop (at least i think i can't). I'm customizing an e-merchandise program (opencart) and my php knowledge isn't enough to tackle the problem.
There is this function which displays the categories from the store. It uses a variable that is constantly updated via $var .= "value".
I'm so far that i know how many sub-categories there are, but i don't know how to apply this range to my HTML.
I'm working towards a situation like below
<ul id="cats">
<li id="cat1">Cat
<ul id="sub1">
<li>item</li>
<li>item</li>
</ul>
</li>
<li id="cat2">Cat
<ul id="sub2">
<li>item</li>
<li>item</li>
</ul>
</li>
</ul>
I don't have a clue how to increase the count of the second unordered lists. Below the code where the second unordered lists are generated.
[..]
$cPiD = strlen($parent_id);
if ($results) {
if ($parent_id == 0) {
$output .= '<ul id="cats">';
} else {
$output .= '<ul id="sub'.$cPiD.'">';
}
}
[..]
The variable $cPiD holds the total amount of sub categories (in this case 2). I want this variable to automatically apply the correct number to the unordered list (so apply id="sub1" to the first unordered list and id="sub2" tot he second one (as in my example above)).
The problem is that i can't use a for() loop after the else part, because in my HTML i wil get two <ul> tags instead of one.
Below the PHP code where it all happens
$category_id = array_shift($this->path);
$output = '';
$results = $this->model_catalog_category->getCategories($parent_id);
$count = 0;
$cPiD = strlen($parent_id);
if ($results) {
if ($parent_id == 0) {
$output .= '<ul id="cats">';
} else {
$output .= '<ul id="sub'.$cPiD.'">';
}
}
foreach ($results as $result) {
$count++;
if (!$current_path) {
$new_path = $result['category_id'];
$output .= '<li id="cat'.$count.'">';
} else {
$new_path = $current_path . '_' . $result['category_id'];
$output .= '<li>';
}
$children = '';
$children = $this->getCategories($result['category_id'], $new_path);
$output .= $result['name'];
$output .= $children;
if (!$current_path) {
$output .= '</li>';
} else {
$output .= '</li>';
}
}
if ($results) {
if ($parent_id == 0) {
$output .= '</ul>';
} else {
$output .= '</ul>';
}
}
Does anybody maybe have an idea how to solve this?
EDIT:
Oh, i tries adding the following construction in the foreach() loop, but that gave problems when a certain categories don't have any sub categories.
if (!$current_path) {
$output .= '$result['name'] . ' <ul id="sub'.$count.'">';
}else{
$output .= $result['name'];
}
You can use this:
// at the top of your code (ouside of the loop)
$cPiD = 1;
// inside the loop you need to increment the parameter
$output .= '<ul id="sub'.$cPiD++.'">';
After each time the item is used, its value will be incremented by 1. (after already using it)