PHP Multi-level menu - php

I have this code that supposed to generate a multi-level menu from my database. My problem I have a specific HTML code when there are/are no child menu.
Here is the HTML Code if the menu does not have a child menu:
<li class="nav-item dropdown">Menu</li>
Here is the HTML code if the menu has a child menu:
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle arrow-none" href="javascript: void(0);" id="topnav-user-access" role="button">
<span key="t-user-access">Parent Menu</span> <div class="arrow-down"></div>
</a>
<div class="dropdown-menu" aria-labelledby="topnav-user-access">
Child Menu 1
Child Menu 2
...
</div>
</li>
Here is my PHP code to generate the menu.
public function generate_multilevel_menu($module_id, $username, $parent_menu = null){
$menu = '';
if(!empty($parent_menu)){
$query = 'SELECT MENU_ID, MENU, PARENT_MENU, IS_LINK, MENU_LINK FROM technical_menu WHERE MODULE_ID = :module_id AND PARENT_MENU = :parent_menu ORDER BY ORDER_SEQUENCE, MENU';
}
else{
$query = 'SELECT MENU_ID, MENU, PARENT_MENU, IS_LINK, MENU_LINK FROM technical_menu WHERE MODULE_ID = :module_id AND (PARENT_MENU IS NULL OR PARENT_MENU = "0") ORDER BY ORDER_SEQUENCE, MENU';
}
$sql = $this->db_connection->prepare($query);
$sql->bindValue(':module_id', $module_id);
if(!empty($parent_menu)){
$sql->bindValue(':parent_menu', $parent_menu);
}
if($sql->execute()){
while($row = $sql->fetch()){
$menu_id = $row['MENU_ID'];
$menu_access_right = $this->check_role_access_rights($username, $menu_id, 'menu');
if($menu_access_right > 0){
if(!empty($row['MENU_LINK'])){
$menu .= ''. $row['MENU'] .'';
#$menu .= '<li class="nav-item dropdown">'. $row['MENU'] .'</li>';
}
else{
$menu .= '<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle arrow-none" href="javascript: void(0);" id="topnav-user-access" role="button">
<span key="t-user-access">'. $row['MENU'] .'</span> <div class="arrow-down"></div>
</a>';
}
$menu .= '<div class="dropdown-menu" aria-labelledby="topnav-user-access">' . $this->generate_multilevel_menu($module_id, $username, $row['MENU_ID']) . '</div>';
$menu .= '</li>';
}
}
return $menu;
}
}
Here is the screenshot of my database structure and details.
Here is the sample output of my code:
My problem is that I can't seem to incorporate the HTML code to fit my code. The code works if the HTML code is a simple HTML list but using my HTML code and conditions I can't seem to fix it. What part of my code do I need to modify to accommodate my if conditions?

Related

Adding categories & subcategories to a blog

Ok, I have done lots of research and looked at lots of stackoverflow questions but none have answered my question.
I'm building a simple blog and am at the moment i'm trying to build a simple categories/subcategories system but have hit an obstacle in making the subcategories load under their parent. If you know an easier method to the one i am doing below please let me know.
Here is how my db is structured:
id || name || parent_id || status
------------------------------------------
1 || category1 || NULL || 1
2 || subcategory1 || 1 || 1
Then i have my model code:
public function getCategories()
{
$results = $this->db->select('msi_items_categories','status = 1 AND parent_id = NULL');
if( !is_array($results[0]) ) {
$new_results = array();
array_push($new_results, $results);
return $new_results;
} else {
return $results;
}
}
public function getSubCategories($parent)
{
$bind = [':parent' => $parent];
$results = $this->db->select('msi_items_categories','status = 1 AND parent_id = :parent');
if( !is_array($results[0]) ) {
$new_results = array();
array_push($new_results, $results);
return $new_results;
} else {
return $results;
}
}
This is my controller code:
public function error()
{
$getSettings = $this->setting->getAll();
$getCategories = $this->setting->getCategories();
$getSubCategories = $this->setting->getSubCategories();
if(is_array($getCategories[0]) ) {
$isCategory = true;
} else {
$isCategory = false;
}
if(is_array($getSubCategories[0]) ) {
$isSubCategory = true;
} else {
$isSubCategory = false;
}
$data = [
'settings' => $getSettings,
'mainCategory' => $getCategories,
'subCategory' => $getSubCategories,
'isCategory' => $isCategory
'isSubCategory' => $isSubCategory
];
$this->view('index', $data);
}
Then I have my templatecode which is where im trying to do the foreach and the top category works fine but i just can't figure out how i do the subcategories. Before i started using the MVC model, i would simply just put the class inside the main category foreach and then put the id but now its all done in the controller so i cannot figure out how i do subcategories using the MVC model.
<?php foreach($data['mainCategory'] as $category) : ?>
<li class="dropdown">
<a class="dropdown-toggle nav-link dropdown-toggle pl-0" data-toggle="dropdown" aria-expanded="false" href="#"><?php echo $category['name']; ?></a>
<div class="dropdown-menu" role="menu">
<a class="dropdown-item" role="presentation" href="#"><i class="text-black-50 fas fa-box"></i> All <?php echo $category['name']; ?></a>
<div class="dropdown-divider" role="presentation"></div>
<?php foreach($data['subCategory'] as $scategory) : ?>
<a class="dropdown-item" role="presentation" href="#"><i class="text-black-50 <?php echo $scategory['icon']; ?>"></i> <?php echo $scategory['name']; ?></a>
<?php endforeach; ?>
</div>
</li>
<?php endforeach; ?>
Thanks very much!
$query = 'SELECT id, parent_id, name, icon FROM categories ORDER BY name';
$result = mysqli_query($conn, $query) OR trigger_error($query.'<br>'.mysqli_error($conn),E_USER_ERROR);
$refs = Array();
$categories = Array();
while($row = mysqli_fetch_assoc($result))
{
$thisref = &$refs[$row['id']];
$thisref['name'] = $row['name'];
$thisref['icon'] = $row['icon'];
if($row['parent_id']) $refs[$row['parent_id']]['children'][$row['id']] = &$thisref;
else $categories[$row['id']] = &$thisref;
}
echo '<ul class="nav navbar-nav mr-auto">';
foreach($categories as $category)
{
echo '<li class="dropdown">';
echo is_array($category['children'])
? '<a class="dropdown-toggle nav-link dropdown-toggle pl-0" data-toggle="dropdown" aria-expanded="false" href="#">'.$category['name'].'</a>'.sub($category['children'])
: $category['name'];
echo '</li>';
}
echo '</ul>';
function sub(&$subCat)
{
echo '<div class="dropdown-menu" role="menu">
<a class="dropdown-item hide-me" role="presentation" href="#"><i class="fas fa-fire mr-2"></i> Most Popular </a>
<div class="dropdown-divider hide-me" role="presentation"></div>';
foreach($subCat as $id => $subcategory)
{
echo '<a class="dropdown-item" role="presentation" href="'.FULL_ROOT.'/category/'.$id.'/"><i class="'.$subcategory['icon'].' mr-2"></i>'.$subcategory['name'].'</a>';
}
echo '</div>';
}

Adding an active class according to URL

I have a php/mysql driven menu. I am trying to add an active class to the menu item whenever the URI reflects that menu item (basically, when I am on that page) but every example I find is with a menu that is not populated through an array or repeater like mine.
How can manipulate the active class you see below so that it shows only when I am on that page?
Here is my menu
<?php
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
if($row['id'] == 2){
// below is the active class that is now being populated accross all items equally
echo '<li class="nav-item dropdown active">';
echo '<a href="#" class="nav-link dropdown-toggle animated fadeIn animation-delay-7" data-toggle="dropdown" data-hover="dropdown" role="button" aria-haspopup="true" aria-expanded="false" data-name="services">';
echo $row["pagename"];
echo '<i class="zmdi zmdi-chevron-down"></i>';
echo '</a>';
// create a new list
echo '<ul class="dropdown-menu">';
// loop second/submenu query results
while($sub_row = $result2->fetch_assoc()) {
echo '<li>';
echo '' . $sub_row["pagename"] . '';
echo '</li>';
}
echo "</ul>";
echo '</li>';
} else { // for all other menu links
echo '<li class="nav-item dropdown active">';
echo '' . $row["pagename"] . '';
echo '</li>';
}
}
} else {
echo "0 results";
}
?>
The screenshot below shows the result after it renders. Notice the active class highlighted in yellow, it is added to every item in my main menu (the way I have it coded, which it's wrong of course). The green is the $sub_row["link"] (to answer Mark's question below
Notice, the active class is only needed to be added programmatically on the main menu. (the else portion of my code).
The screenshot below shows how the active class affects the menu item if the user was in the www.domain.com/how-it-works.php page. The active class contians the styles to make the menu item appear that way.
Thanks in advance
Hard to test with not having the DB outputs to match against mate but try this, basically you want an if statement to get the current page URL and match it against the url you get back from the DB.
<?php
if ($result->num_rows > 0) {
$pageURL = str_replace('/', '', $_SERVER['REQUEST_URI']);
$homeURL = $_SERVER['REQUEST_URI'];
// output data of each row
while($row = $result->fetch_assoc()) {
if($row['id'] == 2){ ?>
<li class="nav-item dropdown">
<a href="" class="nav-link dropdown-toggle animated fadeIn animation-delay-7" data-toggle="dropdown" data-hover="dropdown" role="button" aria-haspopup="true" aria-expanded="false" data-name="services">
<?php echo $row["pagename"];?>
<i class="zmdi zmdi-chevron-down"></i>
</a>
<ul class="dropdown-menu">
<?php while($sub_row = $result2->fetch_assoc()) {?>
<li>
<a href="<?php echo $sub_row["link"]; ?>" class="dropdown-item">
<?php echo $sub_row["pagename"] ?>
</a>
</li>
<?php } ?>
</ul>
</li>
<?php
} else if($row['id'] == 1){ ?>
<li class="nav-item dropdown <?php echo ($homeURL == $row["link"])? 'active' : '';?>">
<a href="<?php echo $row["link"]; ?>" class="nav-link dropdown-toggle animated fadeIn animation-delay-7">
<?php echo $row["pagename"]; ?>
</a>
</li>
<?php
} else { ?>
<li class="nav-item dropdown <?php echo ($pageURL == $row["link"])? 'active' : '';?>">
<a href="<?php echo $row["link"]; ?>" class="nav-link dropdown-toggle animated fadeIn animation-delay-7">
<?php echo $row["pagename"]; ?>
</a>
</li>
<?php }
}
} else {
echo "0 results";
}
?>
Edit:
Bit that gets the current page url is:
$pageURL = end(explode('/', $_SERVER['REQUEST_URI']));
Bit that does the check is (this is just a shorthand if statement:
<?php echo ($pageURL == $row["link"])? 'active' : '';?>

Dynamically insert class="active" into php function that generates bootstrap menu

I have tried pretty much every solution that was available online but cannot get the class="active" to change dynamically for each menu section.
When someone clicks the top level menu item it opens up the second level menu and if someone click the on a second level menu item in that section class="active" needs to remain on the top level menu li tag.
I have the following function that generates my bootstrap menu:
function getMenu() {
global $connection;
mysqli_select_db($connection, "c9");
$query = ("SELECT testName, testId FROM testType");
$result_set = mysqli_query($connection, $query);
while ($row = mysqli_fetch_array($result_set)) {
$testId = $row['testId'];
$testName = $row['testName'];
echo '<li>'; //This is where class="active" needs to be added
echo '<span class="nav-label">'.$testName.'</span>';
echo '<ul class="nav nav-second-level collapse">';
echo '<li>Summary Report</li>';
echo '<li>Add Data</li>';
echo '</ul>';
echo '</li>';
}
}
And the menu structure is as follows:
<ul class="nav metismenu" id="side-menu">
<li class="active">
<a href="/">
<span class="nav-label">Summary</span>
<ul class="nav nav-second-level collapse in">
<li>Reports</li>
</ul>
</li>
<li><a href="#">
<span class="nav-label">Temperature</span>
<ul class="nav nav-second-level collapse">
<li>Summary Report</li>
<li>Add Data</li>
</ul>

pure php or css - tree menu show only active nodes

I have build a tree menu in PHP, using a recursive function, wich works ok.
My menu structure is something like this:
Root
----Categ1
-------Categ11
---------Categ111
---------Categ112
-------Categ12
---------Categ121
---------Categ122
----Categ2
----Categ3
----Categ4
I am using bootstrap.
I need to show only the active nodes and the top level childrens. For my example I need the menu to be opened like this (I clicked on Categ 112 and subcategories of Categ12 to be hidden):
Root
----Categ1
-------Categ11
---------Categ111
---------Categ112
-------Categ12
----Categ2
----Categ3
----Categ4
The php function that generate my menu tree is:
/** show all subcategs of the selected category
* #param $iCategIDSelected
* #param null $arrCategs
* #param bool $bIsOnTheLeaf
* #return string
*/
static function getHTMLCategsForMenuBySelected($iCategIDSelected, $arrCategs=null, $arrAllParents=null){
$bIsVisible = false;
if($arrCategs == null){
$oController = new ShopcategoriesController();
$arrCategs = $oController->getShopCategories(0);
}
//find if we make the current node visible or not
//if the current node id is on the parent's of the selected node then make it visible
//take all nodes starting with root until the selected node
if($arrAllParents==null){
$arrAllParents = ShopcategoriesController::getParentsBySelectedCategID($iCategIDSelected,$arrCategs);
}
$sHTML = '<ul class="nav nav-pills nav-stacked">';
foreach($arrCategs as $oneCateg){
//find if is active or not
$bActiveClass = '';
if(in_array($oneCateg['id'],$arrAllParents)){$bActiveClass = 'active';}
$sHTML .= '<li class="'.$bActiveClass.'">';
$sHTML .= ''.ucfirst($oneCateg['name']).'';
if(count($oneCateg['subCategories'])>0){
$sHTML .= ShopcategoriesController::getHTMLCategsForMenuBySelected($iCategIDSelected, $oneCateg['subCategories'],$arrAllParents);
$sHTML .= '</li>';
}else{
$sHTML .= '';
}
}
$sHTML .= '</ul>';
return $sHTML;
}
The html structure is:
ul.nav > li > a{
padding-left: 30px;
}
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"/>
<div id="menuCateg">
<ul class="nav nav-pills nav-stacked">
<li class="active">
<a class="list-group-item" href="http://localhost:8000/showcategory/1">Categ1</a>
<ul class="nav nav-pills nav-stacked">
<li class="active">
<a class="list-group-item" href="http://localhost:8000/showcategory/2">Categ11 </a>
<ul class="nav nav-pills nav-stacked">
<li class=""><a class="list-group-item" href="http://localhost:8000/showcategory/3">Categ111</a></li>
<li class="active"><a class="list-group-item" href="http://localhost:8000/showcategory/4">Categ112</a></li>
</ul>
</li>
<li class=""><a class="list-group-item" href="http://localhost:8000/showcategory/18">Categ12</a></li>
</ul>
</li>
<li class="">
<a class="list-group-item" href="http://localhost:8000/showcategory/6">Categ2</a>
<ul class="nav nav-pills nav-stacked">
<li class=""><a class="list-group-item" href="http://localhost:8000/showcategory/3">Categ121</a></li>
<li class=""><a class="list-group-item" href="http://localhost:8000/showcategory/4">Categ122</a></li>
</ul>
</li>
<li class=""><a class="list-group-item" href="http://localhost:8000/showcategory/8">Categ3</a>
<li class=""><a class="list-group-item" href="http://localhost:8000/showcategory/8">Categ4</a>
</ul>
</div>
I found the solution in order to generate a menu tree with multilevel subcategories and show only the levels that have been selected (without all children from the tree).
THE TRICK were those lines:
if( //if the current subcateg parentid is on the parent that must be active then show his children's otherwise do not
( in_array($oneCateg['parentid'],$arrAllParents) && in_array($oneCateg['id'],$arrAllParents))
)
Thank you for your help!
/** show all subcategs of the selected category
* #param $iCategIDSelected
* #param null $arrCategs
* #param bool $bIsOnTheLeaf
* #return string
*/
static function getHTMLCategsForMenuBySelected($iCategIDSelected, $arrCategs=null, $arrAllParents=null, $iLevel=0){
$bIsVisible = false;
if($arrCategs == null){
$oController = new ShopcategoriesController();
$arrCategs = $oController->getShopCategories(0);
}
//find if we make the current node visible or not
//if the current node id is on the parent's of the selected node then make it visible
//take all nodes starting with root until the selected node
if($arrAllParents==null){
$arrAllParents = ShopcategoriesController::getParentsBySelectedCategID($iCategIDSelected,$arrCategs);
$arrAllParents[]=0;
}
$iLevel++;
$sHTML = '<ul class="nav nav-pills nav-stacked level_'.$iLevel.'">';
foreach($arrCategs as $oneCateg){
//find if is active or not
$bActiveClass = '';
if(in_array($oneCateg['id'],$arrAllParents)){$bActiveClass = 'active';}
$sHTML .= '<li class="'.$bActiveClass.'">';
$sHTML .= ''.ucfirst($oneCateg['name']).'';
if(count($oneCateg['subCategories'])>0){
if( //if the current subcateg parentid is on the parent that must be active then show his children's otherwise do not
( in_array($oneCateg['parentid'],$arrAllParents) && in_array($oneCateg['id'],$arrAllParents))
) {
$sHTML .= ShopcategoriesController::getHTMLCategsForMenuBySelected($iCategIDSelected, $oneCateg['subCategories'], $arrAllParents, $iLevel);
$sHTML .= '</li>';
}
}else{
$sHTML .= '';
}
}
$sHTML .= '</ul>';
return $sHTML;
}
you should add a class when it's not active like .not-active.
.not-active{
display: none;
}
EDIT
I'm not sure to have understood correctly your point but how about this way:
foreach($arrCategs as $oneCateg){
//find if is active or not
$bActiveClass = '';
if(in_array($oneCateg['id'],$arrAllParents)){ //modified
$bActiveClass = 'active';
}else{
$bActiveClass = 'not-active';
}
$sHTML .= '<li class="'.$bActiveClass.'">';
$sHTML .= ''.ucfirst($oneCateg['name']).'';
if(count($oneCateg['subCategories'])>0){
$sHTML .= ShopcategoriesController::getHTMLCategsForMenuBySelected($iCategIDSelected, $oneCateg['subCategories'],$arrAllParents);
$sHTML .= '</li>';
}else{
$sHTML .= '';
}
}

Active Class in Navbar - Whats wrong with my php?

I'm having a bit of trouble trying to get my head around some php code.
To explain:
I have a navbar made from bootstrap.
I want this nav bar to change it's active class depending on the page.
I have a subnav within the navbar with an li class of 'dropdown'.
When this page is active this li class wants to change to 'dropdown active'
When other pages are active, they just want a simple class of 'active'.
My code is as follows:
PHP:
$pageLoc = 'where';
$nav_items = array('index'=>'Home', 'where'=>'Where?', 'appeals'=>'Current Appeals', 'news'=>'Latest News', 'events'=>'Events', 'dontate'=>'Dontate', );
$nav_sub = array('africa'=>'Africa', 'bangladesh'=>'Bangladesh', 'gaza'=>'Palestine/Gaza', 'kashmir'=>'Kashmir', 'pakistan'=>'Pakistan', 'uk'=>'United Kingdom' );
foreach ($nav_items as $nav_href=>$nav_title) {
if ($pageLoc == $nav_href) && ($pageLoc == 'where') {
echo '<li class="dropdown active">' . $nav_title . '</li>';
}
elseif ($pageHref == $nav_href) && ($pageLoc == 'no') {
echo '<li class="dropdown">' . $nav_title . '</li>';
}
elseif ($pageHref == $nav_href) {
echo '<li class="active">' . $nav_title . '</li>';
}
else {
echo '<li>' . $nav_title . '</li>';
}
}
Navbar:
<ul class="nav pull-right">
<li class="pull-right">
<a class="btn-danger" href="donate"><i class="icon-medkit"> DONATE!</i></a>
</li>
</ul>
<ul class="nav pull-right">
<li class="">
Home
</li>
<li class="dropdown">
<a href="../where/" class="dropdown-toggle disabled" data-toggle="dropdown">
Where?
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li class="nav-header">Where we operate</li>
<li class="divider"></li>
<li>Africa</li>
<li>Bangladesh</li>
<li>Palestine/Gaza</li>
<li>Kashmir</li>
<li>Pakistan</li>
</ul>
</li>
<li class="">
Current Appeals
</li>
<li class="">
Latest News
</li>
<li class="">
Events
</li>
</ul>
I am aware that this php is generating a syntax error. I am no pro unfortunately! :( This is
what I originally had:
foreach ($nav_items as $nav_href=>$nav_title) {
if ($pageHref == $nav_href) {
echo '<li class="active">' . $nav_title . '</li>';
}
else {
echo '<li>' . $nav_title . '</li>';
}
}
You've written:
if ($pageLoc == $nav_href) && ($pageLoc == 'where')
It should say:
if ($pageLoc == $nav_href && $pageLoc == 'where')
On a more general note, the absence of detailed debugging information is a pain, but there are workarounds. For example, if you delete half of the code and try to run it again*, does it still give a syntax error? If so, delete a bit more and see what happens; if not, undelete some and see if it works. Keep trying this and narrowing it down until you find where the error is.
*(obviously, don't delete in such a way that you create syntax errors; for example, if you delete the end of an if clause including the }, you'll get non-matching brackets.)

Categories