php: Generate dynamic menu with CSS code from multidimensional array - php

I'm trying to mark the current requested page as active in my menu but something seems to go wrong... Me and my co-workers can't figure out what's going wrong... I've been trying to figure this one out for more than a week now, I've read what feels like dozens of questions and answers on this and other sites to no avail.
The one thing that really puzzles me is how the value of $page seems to change from a string to a number as you can tell with the debugging I've implemented.
This is the relevant code from index.php:
$pages = array('home','overons','onsteam','organisatie','diensten','aandeslag',
'ictlab','eendagbij','opdrachtgevers','verwijzers',
'trajectbegeleiding','contact');
if(in_array($_GET['p'], $pages)) {
$content = $_GET['p'];
} else {
$content = "home";
}
echo "<!-- content = ".$content." -->\n";
require($inc_path.'navbar'.$php_ext);
and the code from navbar.php:
$menu = array (
'home',
'overons' => array (
'onsteam',
'organisatie'
),
'diensten' => array (
'aandeslag',
'ictlab',
'eendagbij'
),
'opdrachtgevers',
'verwijzers' => array (
'trajectbegeleiding'
),
'contact'
);
$pagenames = array (
'home' => 'Home',
'overons' => 'Over Ons',
'onsteam' => 'Ons Team',
'organisatie' => 'Organisatie',
'diensten' => 'Diensten',
'aandeslag' => 'Aan De Slag',
'ictlab' => 'ICT Lab',
'eendagbij' => 'Een dag bij',
'opdrachtgevers' => 'Opdrachtgevers',
'verwijzers' => 'Verwijzers',
'trajectbegeleiding' => 'Traject begeleiding',
'contact' => 'Contact'
);
function MakeMenu($menu, $currentpage, $level = 0) {
echo "<!-- MakeMenu0: currentpage = ".$currentpage." -->\n";
global $pagenames;
$ret = "";
$indent = str_repeat(" ", $level * 2);
if ($level!=0) {
$ret .= "<!-- MakeMenu1: Level = ".$level." -->\n";
$ret .= sprintf("%s<ul class=\"dropdown-menu\">\n", $indent);
} else {
$ret .= "<!-- MakeMenu2: Level = 0 -->\n";
$ret .= sprintf("%s<ul class=\"nav navbar-nav\">\n", $indent);
}
$indent = str_repeat(" ", ++$level * 2);
foreach ($menu as $page => $subpages) {
if (!is_numeric($page)) {
if ($page==$currentpage) {
$ret .= "<!-- MakeMenu3: page (".$page.") = currentpage (".$currentpage.") -->\n";
if (is_array($subpages)) {
$ret .= sprintf("%s<li class=\"active dropdowm\"><a href='?p=%s'>%s<span class='caret'></span></a>", $indent, $page, $pagenames[$page]);
} else {
$ret .= sprintf("%s<li class=\"active\"><a href='?p=%s'>%s</a>", $indent, $page, $pagenames[$page]);
}
} else {
$ret .= "<!-- MakeMenu4: page (".$page.") != currentpage (".$currentpage.") -->\n";
if (is_array($subpages)) {
$ret .= sprintf("%s<li class=\"dropdown\"><a href='?p=%s'>%s<span class='caret'></span></a>", $indent, $page, $pagenames[$page]);
} else {
$ret .= sprintf("%s<li><a href='?p=%s'>%s</a>", $indent, $page, $pagenames[$page]);
}
}
}
if (is_array($subpages)) {
$ret .= "\n";
$ret .= MakeMenu($subpages, $currentpage, $level + 1);
$ret .= $indent;
} else if (strcmp($page, $subpages)) {
if ($page==$currentpage){
$ret .= "<!-- MakeMenu5: page (".$page.") = currentpage (".$currentpage.") -->\n";
$ret .= sprintf("%s<li class=\"active\"><a href='?p=%s'>%s</a>", $indent, $subpages, $pagenames[$subpages]);
} else {
$ret .= "<!-- MakeMenu6: page (".$page.") != currentpage (".$currentpage.") -->\n";
$ret .= sprintf("%s<li><a href='?p=%s'>%s</a>", $indent, $subpages, $pagenames[$subpages]);
}
}
$ret .= sprintf("</li>\n", $indent);
}
$indent = str_repeat(" ", --$level * 2);
$ret .= sprintf("%s</ul>\n", $indent);
return($ret);
}
echo MakeMenu($menu, $content);
And the very puzzeling output:
<!-- MakeMenu0: currentpage = overons -->
<!-- MakeMenu0: currentpage = overons -->
<!-- MakeMenu0: currentpage = overons -->
<!-- MakeMenu0: currentpage = overons -->
<!-- MakeMenu2: Level = 0 -->
<ul class="nav navbar-nav">
<!-- MakeMenu5: page (0) = currentpage (overons) -->
<li class="active"><a href='?p=home'>Home</a></li>
<!-- MakeMenu3: page (overons) = currentpage (overons) -->
<li class="active dropdowm"><a href='?p=overons'>Over Ons<span class='caret'></span></a>
<!-- MakeMenu1: Level = 2 -->
<ul class="dropdown-menu">
<!-- MakeMenu5: page (0) = currentpage (overons) -->
<li class="active"><a href='?p=onsteam'>Ons Team</a></li>
<!-- MakeMenu6: page (1) != currentpage (overons) -->
<li><a href='?p=organisatie'>Organisatie</a></li>
</ul>
</li>
<!-- MakeMenu4: page (diensten) != currentpage (overons) -->
<li class="dropdown"><a href='?p=diensten'>Diensten<span class='caret'></span></a>
<!-- MakeMenu1: Level = 2 -->
<ul class="dropdown-menu">
<!-- MakeMenu5: page (0) = currentpage (overons) -->
<li class="active"><a href='?p=aandeslag'>Aan De Slag</a></li>
<!-- MakeMenu6: page (1) != currentpage (overons) -->
<li><a href='?p=ictlab'>ICT Lab</a></li>
<!-- MakeMenu6: page (2) != currentpage (overons) -->
<li><a href='?p=eendagbij'>Een dag bij</a></li>
</ul>
</li>
<!-- MakeMenu6: page (1) != currentpage (overons) -->
<li><a href='?p=opdrachtgevers'>Opdrachtgevers</a></li>
<!-- MakeMenu4: page (verwijzers) != currentpage (overons) -->
<li class="dropdown"><a href='?p=verwijzers'>Verwijzers<span class='caret'></span></a>
<!-- MakeMenu1: Level = 2 -->
<ul class="dropdown-menu">
<!-- MakeMenu5: page (0) = currentpage (overons) -->
<li class="active"><a href='?p=trajectbegeleiding'>Traject begeleiding</a></li>
</ul>
</li>
<!-- MakeMenu6: page (2) != currentpage (overons) -->
<li><a href='?p=contact'>Contact</a></li>
</ul>

Well regarding puzzling behavior:
The one thing that really puzzles me is how the value of $page seems
to change from a string to a number as you can tell with the debugging
I've implemented.
If you look closely at this line:
foreach ($menu as $page => $subpages) {
you will see that key of an array is assigned to $page. From your array:
$menu = array (
'home',
'overons' => array(
'onsteam',
'organisatie'
),
'diensten' => array(
'aandeslag',
'ictlab',
'eendagbij'
),
'opdrachtgevers',
'verwijzers' => array(
'trajectbegeleiding'
),
'contact'
);
we can see that some of pages are keys, and some are values. That is why you have sometimes pages names and sometimes array indexes.
You can define your array as following to prevent this behavior:
$menu = [
'home' => null,
'overons' => [
'onsteam' => null,
'organisatie' => null
],
'diensten' => [
'aandeslag' => null,
'ictlab' => null,
'eendagbij' => null
],
'opdrachtgevers' => null,
'verwijzers' => [
'trajectbegeleiding' => null
],
'contact' => null
];
Going further you have a really complicated recursive function. I would suggest using PHP Iterators. In particular RecursiveArrayIterator alongside with RecursiveIteratorIterator, that can be extended like this:
class UlRecursiveIteratorIterator extends RecursiveIteratorIterator
{
public function beginIteration()
{
echo '<ul class="nav navbar-nav">', PHP_EOL;
}
public function endIteration()
{
echo '</ul>', PHP_EOL;
}
public function beginChildren()
{
echo str_repeat(' ', $this->getDepth() + 1), '<ul class="dropdown-menu">', PHP_EOL;
}
public function endChildren()
{
echo str_repeat(' ', $this->getDepth() + 1), '</ul>', PHP_EOL;
echo str_repeat(' ', $this->getDepth()), '</li>', PHP_EOL;
}
}
Having this iterator, your function can be refactored to one simple foreach loop:
$iterator = new RecursiveArrayIterator($menu);
$iterator = new UlRecursiveIteratorIterator(
$iterator,
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $page => $_) {
$active = false;
// Used for pritty print only. Remove in production.
$depth = $iterator->getDepth();
$line = str_repeat(' ', ($depth + 1) + ($depth === 0 ? 0 : 1));
if ($page === $content) {
$active = true;
}
$class = $active ? 'active' : '';
if ($iterator->callHasChildren()) {
$line .= "<li class=\"$class dropdown\"><a href='?p=$page'>{$pagenames[$page]}<span class='caret'></span></a>";
} else {
$line .= "<li class=\"$class\"><a href='?p=$page'>{$pagenames[$page]}</a>";
}
echo $line, PHP_EOL;
}
Here is the demo.

Related

Dynamic ID with JQUERY functions

I have a problem with my JQUERY code. Here's the code:
$maxcounter = $( '.testimonio-popup[id^="popup-"]' ).length;
var i = 0;
while (i < $maxcounter) {
$('#open-'+i).on('click', function() {
$('#popup-'+i).fadeIn('slow');
$('.testimonio-overlay').fadeIn('slow');
$('.testimonio-overlay').height($(window).height());
return false;
});
$('#close-'+i).on('click', function(){
$('#popup-'+i).fadeOut('slow');
$('.testimonio-overlay').fadeOut('slow');
return false;
});
i++;
}
It takes correctly the total of times the .testimonio-popup div is appearing in the site, and the fadeIn action for .testimoniooverlay class works.
But the fadeIn action for #popup-[number] is not working. Any help why?
For further assistance, I attach the PHP code that makes the query:
function get_the_content_with_formatting ($more_link_text = '(more...)', $stripteaser = 0, $more_file = '') {
$content = get_the_content($more_link_text, $stripteaser, $more_file);
$content = apply_filters('the_content', $content);
$content = str_replace(']]>', ']]>', $content);
return $content;
}
add_shortcode( 'testimonios', 'display_testimonios' );
function display_testimonios($atts){
$atributos = shortcode_atts([ 'grupo' => 'PORDEFECTO', ], $atts);
$buffer = '<div class="testimonio-overlay"></div> ';
$contadorid = 0;
$q = new WP_Query(array(
'post_type' => 'test',
'tax_query' => array(
array(
'taxonomy' => 'grupos_testimonios',
'field' => 'slug',
'terms' => $atributos['grupo']
//'terms' => 'grupo-1'
)
)
) );
while ($q->have_posts()) {
$q->the_post();
$buffer .= '<div class="testimonio">';
$buffer .= '<div class="testimonio-img">' . get_the_post_thumbnail($_post->ID).'</div>';
$buffer .= '<div class="testimonio-titulo"><p>' . get_the_title() .'</p></div>';
$buffer .= '<div class="testimonio-intro"><div class="testimonio-parrafo">' . get_the_excerpt() . '</div><button class="testimonio-boton" id="open-'.$contadorid.'">Leer más</button></div></div>';
$buffer .= '<div class="testimonio-popup" id="popup-'.$contadorid.'"><div class="testimonio-popup-contenido"><div class="testimonio-cerrar"><button class="testimonio-boton-cerrar" id="close-'.$contadorid.'">x</button></div>';
$buffer .= '<div class="testimonio-popup-titulo"><p>' . get_the_title() .'</p></div>';
$buffer .= '<div class="testimonio-popup-contenido">' . get_the_content_with_formatting() . '</div></div></div>';
$contadorid = $contadorid + 1;
}
wp_reset_postdata();
return $buffer;
}
Thank you!
Frede
#Rory McCrossan is right (see comment).
I'm not sure what goes wrong there, but I would suggest you change this logic:
$("#open-1").on(....);
$("#open-2").on(....);
$("#open-3").on(....);
$("#close-1").on(....);
$("#close-2").on(....);
$("#close-3").on(....);
$("#popup-1").fadeIn()
$("#popup-2").fadeIn()
to using classes and attributes:
$(".popup-handler").on(.. check if open/close -> then action..);
$(".popup").filter(.. check for specific reference..).fadeIn()
And if you want to interact elements with different classes, add data attributes to them so you can find them when needed. Here is a simple example:
<!-- popup nr 1 -->
<div class="popup-handler" data-rel="1" data-action="open"></div>
<div class="popup" data-rel="1">
<div class="popup-handler" data-rel="1" data-action="close"></div>
</div>
<!-- popup nr 2 -->
<div class="popup-handler" data-rel="2" data-action="open"></div>
<div class="popup" data-rel="2">
<div class="popup-handler" data-rel="2" data-action="close">x</div>
</div>
<!-- jQuery -->
$(".popup-handler").on("click", function() {
/* get popup reference & action */
var rel = $(this).data("rel"),
action = $(this).data("action");
/* find the target popup */
var $popup = $(".popup").filter("[data-rel=" + rel + "]");
if (action == "open") {
$popup.fadeIn("slow");
/* ... other code when opening a popup... */
}
if (action == "close") {
$popup.fadeOut("slow");
/* ... other code when closing a popup... */
}
});
Demo here - 4 popups, working: jsfiddle
(Defining the same functions inside a while loop is generally a bad idea.)

PHP breadcrumb array, link is not full

I go the following code for breadcrumb :
<?php
class Breadcrumb
{
private $breadcrumb;
private $separator = ' / ';
private $domain = 'example.org';
public function build($array)
{
$breadcrumbs = array_merge(array('Home' => ''), $array);
$count = 0;
foreach($breadcrumbs as $title => $link) {
$this->breadcrumb .= '
<span itemscope="" itemtype="https://schema.org/BreadcrumbList">
<a href="'.$this->domain. '/'.$link.'" itemprop="url">
<span itemprop="title">'.$title.'</span>
</a>
</span>';
$count++;
if($count !== count($breadcrumbs)) {
$this->breadcrumb .= $this->separator;
}
}
return $this->breadcrumb;
}
}
?>
I call it as follow:
<?php
$breadcrumb = new Breadcrumb();
echo $breadcrumb->build(array(
$pageTitle => 'about',
'More' => 'more.php'
));
?>
pageTitle is a var on top of each page.
The output is correct and shows: Home / About / More
but, the link on each one of them is as follow:
Home: example.org
About: example.org/about
More: example.org/more.php
And I am looking for output like that: example.org/about/more.php
Thank you very much!
You can concatenate the links as you progress through your loop...
$bclink = '';
foreach($breadcrumbs as $title => $link) {
if ($link != '') {
$bclink .= '/' . $link;
}
$this->breadcrumb .= '
<span itemscope="" itemtype="https://schema.org/BreadcrumbList">
<a href="'.$this->domain.$bclink.'" itemprop="url">
<span itemprop="title">'.$title.'</span>
</a>
</span>';
$count++;
if($count !== count($breadcrumbs)) {
$this->breadcrumb .= $this->separator;
}
}

How can I remove nested nav from all top-level listed items, except for Services in a PHP array?

I am trying to remove the nested list ($subPages) from displaying under all of the top-level lists ($pages), except for Services; however my code is allowing the nested list items to display under all three top-level list items.
Can anyone help me remove the nested list items from all top-level list items, except for one?
NB. I am new to PHP, and would like to just stick to PHP without any other languages. My PHP code may be in efficient; if you spot room for improvement, let me know.
<nav class="navBar wAuto fRight positAb">
<?php
$pages = array(
'/about/' => 'About',
'/services/' => 'Services',
'/book-a-service/' => 'Book a service'
);
$subPages = array(
'/services/bathroom-installation' => 'Bathroom installation',
'/services/boiler-repair' => 'Boiler repair',
'/services/boiler-service' => 'Boiler service',
'/services/gas-oil' => 'Gas & oil',
'/services/heat-recovery' => 'Heat recovery',
'/services/heating-plumbing' => 'Heating and plumbing',
'/services/rainwater-harvesting' => 'Rainwater harvesting',
'/services/solar-solutions' => 'Solar solutions',
'/services/underfloor-heating' => 'Underfloor heating'
);
$activePage = $_SERVER['REQUEST_URI'];
echo '<ul>';//open nav
foreach( $pages as $url => $anchor){
$activeClass = ($_SERVER['REQUEST_URI'] == $url) ? " active" : "";
echo '<li class="fLeft"><a class="font13'.$activeClass.'" href="'.$url.'"';
if($url == '/services/'){
echo 'id="parent"';
}
if($url == '/book-a-service/')
echo 'id="ctaUHP"';
echo '>'.$anchor.'</a>';
//Open nested nav
if(isset($url) == '/services/'){
echo '<ul class="wAuto positAb">';
foreach ($subPages as $url => $anchor){
echo '<li class="fullWidth"><a class="font10" href="'.$url.'"';//open nested nav
echo '>'.$anchor.'</a></li>';
}
};
echo '</li></ul>'; //close nested nav
}
//Close submenu
echo '</ul>';
//close nav
?>
</nav>
In the code for open nested nav, change if(isset($url) == '/services/') to if($url == '/services/'){
Explanation: You don't need to verify if the $url is set, you just need to print the nested nav when your $url == '/services/', that's why you need to remove the function isset() :)
EDIT
You lacked some {} and I even rearranged some of your code, so now looks like this :)
echo '<ul>'; //open nav
foreach( $pages as $url => $anchor) {
$activeClass = ($_SERVER['REQUEST_URI'] == $url) ? " active" : "";
echo '<li class="fLeft"><a class="font13'.$activeClass.'" href="'.$url.'"';
if($url == '/services/') {
echo 'id="parent"';
}
if($url == '/book-a-service/') {
echo 'id="ctaUHP"';
}
echo '>'.$anchor.'</a>';
//verify if exists nested nav
if($url == '/services/') {
echo '<ul class="wAuto positAb">'; //open nested nav
foreach ($subPages as $url => $anchor) {
echo '<li class="fullWidth"><a class="font10" href="'.$url.'">'.$anchor.'</a></li>';
}
echo '</ul>'; //close nested nav
}
echo '</li>';
}
echo '</ul>'; //close nav

Split menu list in two every 7 items

I got a dynamic menu that uses bootstrap, but one menu button has about 14 child items. Way too long for a website, so I want to split it in half.
This is the code that I'm trying to replicate:
<ul class="dropdown-menu dropdown-menu-large row" role="menu">
<li class="col-sm-6">
<ul>
<li>Life Insurance</li>
<li>Home Insurance</li>
<li>Travel Insurance</li>
<li>Pet Insurance</li>
</ul>
</li>
<li class="col-sm-6">
<ul>
<li>Boat Insurance</li>
<li>Auto Insurance</li>
<li>Bike Insurance</li>
<li>Business Insurance</li>
</ul>
</li>
</ul>
This is what I tried:
$tel = 1;
foreach ( $nmenuccr as $cmenu ) {
// If id matches and category id is 11 (services) split in half
if ( $cmenu['id'] && $cmenu['catid'] == '11' ){
if($tel == 1) {
$hmenu .= '<li class="col-sm-6"><ul>';
}
$hmenu.= '
<li>'.$cmenu['title'].'</li>
';
if(($tel % 7) == 0){
$hmenu .= '</ul></li> <li class="col-sm-6"><ul>';
}
$tel++;
if(($tel % 7) != 0){
$menu .= '</li>';
}
//Else use the normal dropdown layout
}else{
if (strlen($cmenu['title']) > 25){
$shortstrmen = substr($cmenu['title'], 0, 25) . '...';
$hmenu.= '
<li>'.$shortstrmen.'
';
}else{
$hmenu.= '
<li>'.$cmenu['title'].'
';
}
}
}
However this returns the following code:
https://jsfiddle.net/sms16v44/
Does anyone see what I am doing wrong?
Instead of trying to alter your code, I came up with the following. This allows you to automatically create sub menu's out of a menu. I hope it helps out.
It creates it like the following screenshot:
and the other function/way:
<?php
// recreating dummy menu data
function createDummyList() {
$nmenuccr = array();
for($i=0;$i<24;$i++) {
$a = array();
$a['id']=$i;
$a['catid']=$i;
$a['alias']="alias $i";
$a['title']="title $i";
array_push($nmenuccr, $a);
}
return $nmenuccr;
}
/**
* Parse a menu into smaller menu's
* #param type $menu the main menu
* #param type $class html class
* #param type $max_sub_items max items before creating a new submenu
* #param type $submenu (used for recursion)
* #return string
*/
function createMenuList(&$menu, $class, $max_sub_items) {
$out = "<ul class='$class'><li>";
// shift items
$submenu = array();
$i=0;
$loop=0;
while(count($menu) > 0) {
array_push($submenu, array_shift($menu));
$i++;
if ($i == $max_sub_items || count($menu) == 0) {
$out .= createList($submenu, $loop);
$out .= '<li>In between</li>';
$i=0;
$submenu = array();
$loop++;
}
}
$out .= "</li></ul>";
return $out;
}
/**
*
* #param type $list
* #param type $submenu_label
* #return string
*/
function createList(&$list, $loop) {
$out = '<ul>';
foreach($list as $l) {
$out .= '<li>'.$l['title'].'</li>';
}
$out .= '</ul>';
return $out;
}
/**
* Parse a menu into smaller menu's
* #param type $menu the main menu
* #param type $class html class
* #param type $max_sub_items max items before creating a new submenu
* #param type $submenu (used for recursion)
* #return string
*/
function createSubMenuList(&$menu, $class, $max_sub_items, $submenu = 0) {
// create new list
$out = "<ul class='$class'>";
// shift items
for($i=0;$i<$max_sub_items;$i++) {
$item = array_shift($menu);
// add item to list
if (isset($item)) {
$out .= '<li>'.$item['title'].'</li>';
}
}
// check if we're done
if (count($menu) > 0) {
$submenu++;
$out .= "<li class='submenu_$submenu'>";
// create submenu in parent menu
$out .= createSubMenuList($menu, $class, $max_sub_items, $submenu);
$out .= "</li>";
}
$out .= "</ul>";
return $out;
}
// call menu creation function
$list1=createDummyList();
$list2=createDummyList();
echo createSubMenuList($list1, 'hoofdmenu', 7);
echo "<hr />";
echo createMenuList($list2, 'hoofdmenu', 7);
?>
It would be easier to help if you could include the output array as comment too.
However, try this. Change this:
if(($tel % 7) != 0){
$menu .= '</li>';
}
to:
if(($tel % 7) != 0){
$menu .= '</ul></li>';
}
Hopefully it will work
Keepin it a junky piece of code, you can just quick-fix it this way:
$tel = 0;
/* .. */
// If id matches and category id is 11 (services) split in half
if ( $cmenu['id'] && $cmenu['catid'] == '11' ){
if(++$tel == 1) {
$hmenu .= '<li class="col-sm-6"><ul>';
}
$hmenu.= '<li>'.$cmenu['title'].'</li>';
if(($tel % 7) == 0){
if ($tel == 7) {
$hmenu .= '</ul></li><li class="col-sm-6"><ul>';
} else {
$hmenu .= '</ul></li>';
}
}
//Else use the normal dropdown layout
} // ..
Working example (updated):
http://sandbox.onlinephpfunctions.com/code/13fd6974cd66c847747f44a4be9b892aa47e4979
But you should refactor your $nmenuccr through a function, which will make it an array reflecting the target structure of menu to KISS the view-generating part of code.
Edit: updated operations on $tel.
Try this for any length of menu.....
<ul class="dropdown-menu dropdown-menu-large row" role="menu">
<?php
$counter = 1;
foreach ($nmenuccr as $cmenu ) {
if($counter == 1){
echo '<li class="col-sm-6"><ul>';
}//.... end of if() .....//
echo '<li>'.$cmenu['title'].'</li>';
$counter ++;
if($counter == 5){
echo "</ul></li>";
$counter = 1;
}//.... end of if() .....//
}//.... end of foreach() .....//
?>
</ul>
Your check for category ID 11 didn't seem pertinent to your question, so I didn't include that logic in there. Things become a lot simpler when you leave the opening and closing parts outside the loop. Then, you're only outputting your repeated item, and checking for the one case ($tel % 4) where you put a divider in.
<?php
$nmenuccr = [ // sample data
["alias"=>"life-insurance", "title"=>"Life Insurance"],
["alias"=>"life-insurance", "title"=>"Home Insurance"],
["alias"=>"life-insurance", "title"=>"Travel Insurance"],
["alias"=>"life-insurance", "title"=>"Pet Insurance"],
["alias"=>"life-insurance", "title"=>"Boat Insurance"],
["alias"=>"life-insurance", "title"=>"Auto Insurance"],
["alias"=>"life-insurance", "title"=>"Bike Insurance"],
["alias"=>"life-insurance", "title"=>"Business Insurance"],
];
$hmenu = "<li class=\"col-sm-6\">\n\t<ul>\n";
foreach ($nmenuccr as $tel=>$cmenu) {
if ($tel % 4 == 0 && $tel > 0) {
$hmenu .= "\t</ul>\n</li>\n<li class=\"col-sm-6\">\n\t<ul>\n";
}
$hmenu.= "\t\t<li>$cmenu[title]</li>\n";
}
$hmenu .= "\t</ul>\n</li>";
echo $hmenu;
Output:
<li class="col-sm-6">
<ul>
<li>Life Insurance</li>
<li>Home Insurance</li>
<li>Travel Insurance</li>
<li>Pet Insurance</li>
</ul>
</li>
<li class="col-sm-6">
<ul>
<li>Boat Insurance</li>
<li>Auto Insurance</li>
<li>Bike Insurance</li>
<li>Business Insurance</li>
</ul>
</li>

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>";
?>

Categories