Creating navigation from a multidimensional array

Question: How to create a navigation that allows you to apply different classes to different subitems from a multidimensional array?

Here's how I did it before I needed layered navigation:

Home Pics About 

and was called by nav ():

 function nav(){ $links = array( "Home" => "home.php", "Pics" => "pics.php", "About" => "about.php" ); $base = basename($_SERVER['PHP_SELF']); foreach($nav as $k => $v){ echo buildLinks($k, $v, $base); } } 

Here is the buildLinks ():

 function buildLinks($name, $page, $selected){ if($selected == $page){ $theLink = "<li class=\"selected\"><a href=\"$page\">$name</a></li>\n"; } else { $thelink = "<li><a href=\"$page\">$name</a></li>\n"; } return $thelink; } 

My question is again:

How could I achieve the following navigator (and notice that visible sub-navigation elements are only present on this particular page):

 Home something1 something2 Pics About 

and...

 Home Pics people places About 

What i tried

From looking at it, it seems that some iterator in SPL would be good for this, but I'm not sure how to approach this. I played with RecursiveIteratorIterator, but I'm not sure how to apply a different style only to the submenu items, and also how to show these items only if you are on the right page.

I built this array for testing, but I don’t know how to work with submenu items individually:

 $nav = array( array( "Home" => "home.php", "submenu1" => array( "something1"=>"something1.php", "something2" => "something2.php") ), array("Pics" => "pics.php"), array("About" => "about.php") ); 

Next, the batch will be printed in order, but how can I apply, say, a class name to submenu items or show them only when a person is turned on, say, on the "Home" page?

 $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($nav)); foreach($iterator as $key=>$value) { echo $key.' -- '.$value.'<br />'; } 

And this is me:

 Home something1 something2 Pics About 

But I don’t have the opportunity to apply classes to these subitems and I can’t display them only conditionally, because I don’t see how to focus only on these elements.

+7
iterator php navigation
source share
7 answers

You were on the right track with RecursiveIteratorIterator. It essentially smooths a recursive iterator. Here is the correct way:

 $nav = array( array( "Home" => "home.php", "submenu1" => array( "something1"=>"something1.php", "something2" => "something2.php") ), array("Pics" => "pics.php"), array("About" => "about.php"), ); $it = new RecursiveIteratorIterator( new RecursiveArrayIterator($nav), RecursiveIteratorIterator::SELF_FIRST ); foreach ($it as $k => $v) { if ($it->getDepth() == 0) continue; echo str_repeat(" ", $it->getDepth() - 1) . "$k => $v\n"; } 

gives

 Home => home.php submenu1 => Array something1 => something1.php something2 => something2.php Pics => pics.php About => about.php 
+1
source share

Do not reinvent the wheel, use Zend_Navigation , and you will be happy.

+3
source share

The simplest way, IMHO, is to simply make a recursive call and use a tree-like structured description of your navigation (i.e. nested arrays). Unverified code example:

 <?php $links = array( "Home" => array("home.php", array( "something1"=> array("something1.php", array()), "hello"=> array("hello.php", array( "world" => array("world.php", array()), "bar" => array("bar.php", array()), )), )), "Pics" => array("pics.php", array( "people"=>"people.php", "places" => "places.php", )), "About" => array("about.php", array()), // example no subitems ); // use the following $path variable to indicate the current navigational position $path = array(); // expand nothing $path = array('Home'); // expand Home $path = array('Home', 'hello'); // also expand hello in Home // map indent levels to classes $classes = array( 'item', 'subitem', 'subsubitem', ); // recursive function to build navigation list function buildNav($links, $path, $classes) { // selected page at current level // NOTE: array_shift returns NULL if $path is empty. // it also alters the array itself $selected = array_shift($path); $class = array_shift($classes); echo "<ul>\n"; foreach($links as $name => $link) { list($href, $sublinks) = $link; if ($name == $selected) { echo "<li class=\"selected $class\"><a href=\"$href\">$name</a>\n"; // recursively show subitems // NOTE: path starts now with the selected subitem buildNav($sublinks, $path, $classes); echo "</li>\n"; } else { echo "<li><a href=\"$href\" class=\"$class\">$name</a></li>\n"; } } echo "<ul>\n"; } // actually build the navigation buildNav($links, $path, $classes); ?> 
+2
source share

It looks like you can do it in a more object oriented way. If not, it looks like you should at least define an algorithm that makes sense, right now you are just blindly guessing. Instead, DEFINE.

For example:

I determine that my navigation is a php hash based tree. The navigation item will have the following:

A) if there is a top-level link, the hash array will contain an element (auxiliary array) with the inscription "navigation sheet"

b) The navigation sheet will contain elements labeled "Display value", "link value" and "alt value". These elements will be used to create the anchor tag.

c) if the item has a submenu, in addition to the contents of the “navigation sheet”, the item “sub-navigation” will be present. A subnavigation element will have a “navigation sheet” if it has a displayed navigation element.

Then you can write functions / methods that will display your navigation based on your definition.

+1
source share

What I would do is something like that:

 class MenuItem { protected $active = false; protected $children = array(); protected $name = ''; protected $link = ''; public function __construct($name, $link, $active) {} public function __toString() { //render this item $out = ''; #render here if (!$this->isActive()) { return $out; } $out .= '<ul>'; foreach ($this->children as $child) { $out .= (string) $child; } $out .= '</ul>'; return $out; } public function isActive() { if ($this->active) { return true; } foreach ($this->children as $child) { if ($child->isActive()) { return true; } } return false; } } 

Then all you have is a set of root menu items in the array ... To create your menu, you simply do:

 $rootItems = array($item1, $item2); $out = '<ul>'; foreach ($rootItems as $item) { $out .= (string) $item; } $out .= '</ul>'; 

I will leave the semantics of constructing an object, adding children, etc. to the user ...

+1
source share

How to rewrite the navigation function as follows:

 function nav($links, $level){ foreach($links as $k => $v) { if (is_array($v)) { nav($v, $level + 1) } else { echo buildLinks($k, $v, $base); } } } 

And what to call him:

 $links = array( array( "Home" => "home.php", "submenu1" => array( "something1"=>"something1.php", "something2" => "something2.php") ), array("Pics" => "pics.php"), array("About" => "about.php") ); nav($links, 0); 
+1
source share

@catchmeifyoutry

Thank you, you saved my life LoL.

I changed your function a bit to adapt it to my use, and it worked out:

 $html['navi'] = array( "Home" => "/home/", "DJs & Shows" => "/djs-shows/", "Playlists" => "/playlists/", "Newsbeat" => "/newsbeat/", "Reviews" => "/reviews/", "TV" => "/tv/", "Contact" => "/contact/", "Test" => array("/test/", array("Submenu 1" => "/test/link1", "Submenu 2" => "/test/link2", "Submenu 3" => "/test/link3", "Submenu 4" => "/test/link4", "Submenu 5" => "/test/link5", "Submenu 6" => "/test/link6" ) ) ); $classes = array( 'first-level', 'second-level', 'third-level', ); function siteNavi($links, $classes) { // The best way for MultiArray navigation (LOVE IT!) // Array Shift selects first element and removes it from array $class = array_shift($classes); echo "<ul class=\"$class\">\n"; foreach($links as $name => $link) { if (is_array($link) AND $class != "") { list($link, $sublinks) = $link; if ($_GET['site'] == basename($link)) { $selected = ' class="current"'; } else { $selected = ""; } echo "<li{$selected}><a href=\"{$link}\">{$name}</a>\n"; // recursively show subitems // NOTE: path starts now with the selected subitem siteNavi($sublinks, $classes); echo "</li>\n"; } else { if ($_GET['site'] == basename($link)) { $selected = ' class="current"'; } else { $selected = ""; } echo "<li{$selected}><a href=\"{$link}\" >{$name}</a></li>\n"; } } echo "</ul>\n"; } 

Many thanks!

I wonder what effect such code has on page speed? A few microseconds of milliseconds: D

+1
source share

All Articles