kazu Posted August 3, 2015 Share Posted August 3, 2015 I'm just working on my first ProcessWire project. Unfortunately I am not so talented in the PHP programming, so I have a question about the menu code.In my template it’s just included so: <nav class="row main-navigation"> <ul class='topnav'> <?php // top navigation consists of homepage and its visible children foreach($homepage->and($homepage->children) as $item) { if($item->id == $page->rootParent->id) { echo "<li class='current'>"; } else { echo "<li>"; } echo "<a href='$item->url'>$item->title</a></li>"; } // output an "Edit" link if this page happens to be editable by the current user if($page->editable()) echo "<li class='edit'><a href='$page->editUrl'>" . __('Edit') . "</a></li>"; ?> </ul> </nav> The main menu is displayed, the submenu does not work: <ul class="topnav"> <li class="current"> <a href="/about/">About</a> </li> There are two child sites, and I would like that it is appears like that: <li class="parent">submenu <ul> <li> <a href="/about/child/">submenu item 1</a> </li> <li> </ul> </li> Currently, the submenu will be displayed in the Sidebar. How do I get it in the main menu? Link to comment Share on other sites More sharing options...
Roope Posted August 3, 2015 Share Posted August 3, 2015 Hello kazu and welcome to the forum! If you'd like to output submenu for the current page, change the code like so: // iterate children of a current page object foreach($page->children as $item) as $item) { if($item->id == $page->id) { echo "<li class='current'>"; } else { echo "<li>"; } echo "<a href='$item->url'>$item->title</a></li>"; } And here's how to put it to the main menu: foreach($homepage->and($homepage->children) as $item) { // class name for item wrapper $class = $item->id == $page->rootParent->id ? 'current ' : ''; if($item->hasChildren()) $class .= 'parent '; // open item wrapper if(!empty($class)) { echo "<li class='". rtrim($class) ."'>"; } else { echo "<li>"; } // item link element echo "<a href='$item->url'>$item->title</a>"; // markup for possible childs if($item->hasChildren()) { echo "<ul>"; foreach($item->children as $child) { echo "<li><a href='$child->url'>$child->title</a></li>"; } echo "</ul>"; } // close item wrapper echo "</li>"; } If you like to have 'current' class for submenu items also, change the child markup of foreach loop to match with the first example. 1 Link to comment Share on other sites More sharing options...
kazu Posted August 4, 2015 Author Share Posted August 4, 2015 That does work fine, thanks Roope! I'm trying now to integrate the current status into the childs menu. I have not figured out why the menu knows which page is currently active? I think that probably one if or else is necessary, so that not all pages get the current status? For me a good deal to learn. Once again, thanks for your help! Link to comment Share on other sites More sharing options...
Roope Posted August 5, 2015 Share Posted August 5, 2015 Yes, while we loop the childs, we can check if the current $child id matches with the viewed $page id. // markup for possible childs if($item->hasChildren()) { echo "<ul>"; foreach($item->children as $child) { if($child->id == $page->id) { echo "<li class='current'>"; } else { echo "<li>"; } echo "<a href='$child->url'>$child->title</a></li>"; } echo "</ul>"; } $page is one of the predefined variables in processwire template files and it represents the current viewed page object. https://processwire.com/api/variables/ https://processwire.com/api/variables/page/ Logic is same when we set the class name for first the level item, except that instead of if else we use PHP Ternary Operator and check the $item id against $page->rootParent which represents the parent page closest to the homepage. // class name for item wrapper $class = $item->id == $page->rootParent->id ? 'current ' : ''; This way 'current' class name is added also when child of the item page is being viewed. <ul> <li class='current'> <a href='/item/'>Item</a> <ul> <li class='current'> <a href='/child/'>Child</a> </li> </ul> </li> </ul> If you don't like this behaviur and just want to actual current viewed page wrapper have this class name you can simply change the condition from $page->rootParent to $page: // class name for item wrapper $class = $item->id == $page->id ? 'current ' : ''; Link to comment Share on other sites More sharing options...
EvanFreyer Posted August 5, 2015 Share Posted August 5, 2015 (edited) While Roope's solution works, you will only get the direct children in your nav. You could unify this with a recursive function. Modifying Roope's code like this should work: function buildNav($page) { echo "<ul>"; foreach($page->and($page->children) as $item) { // class name for item wrapper $class = $item->id == $page->rootParent->id ? 'current ' : ''; if($item->hasChildren()) $class .= 'parent '; // open item wrapper if(!empty($class)) { echo "<li class='". rtrim($class) ."'>"; } else { echo "<li>"; } // item link element echo "<a href='$item->url'>$item->title</a>"; // markup for possible childs if($item->hasChildren()) { buildNav($item); } // close item wrapper echo "</li>"; } echo "</ul>"; } I have not tested the code, but it should work. It could be, that you have to tinker with the markup a little bit. Edit: Whoops, wrong variable name. Edited August 5, 2015 by EvanFreyer Link to comment Share on other sites More sharing options...
kazu Posted August 5, 2015 Author Share Posted August 5, 2015 @Roope I have inserted your code, it works fine. Thanks to the links I was able to add also the title <ul class='topnav'> <?php // top navigation consists of homepage and its visible children foreach($homepage->and($homepage->children) as $item) { // class name for item wrapper $class = $item->id == $page->rootParent->id ? 'current ' : ''; if($item->hasChildren()) $class .= 'parent '; // open item wrapper if(!empty($class)) { echo "<li class='". rtrim($class) ."'>"; } else { echo "<li>"; } // item link element echo "<a title='{$page->parent->title}' href='$item->url'>$item->title</a>"; // markup for possible childs if($item->hasChildren()) { echo "<ul>"; foreach($item->children as $child) { if($child->id == $page->id) { echo "<li class='current'>"; } else { echo "<li>"; } echo "<a title='{$child->title}' href='$child->url'>$child->title</a></li>"; } echo "</ul>"; } // close item wrapper echo "</li>"; } ?> </ul> @EvanFreyer In my site it does not work, thanks anyway. Link to comment Share on other sites More sharing options...
EvanFreyer Posted August 5, 2015 Share Posted August 5, 2015 Yeah, I did not change the variable name. Furthermore, you need to call the function in your template. So you should insert another line after the code: buildNav($homepage); Link to comment Share on other sites More sharing options...
Roope Posted August 6, 2015 Share Posted August 6, 2015 If you need recursive version of this, I think you need to modify code from @EvanFreyer a bit: <?php function buildNav(PageArray $pages) { $str = ''; foreach($pages as $item) { // class name for item wrapper // NOTE: since inside function we need to use wire() to get the current viewed page $class = $item->id == wire('page')->rootParent->id ? 'current ' : ''; if($item->hasChildren() && $item->id != 1) $class .= 'parent '; // open item wrapper if(!empty($class)) { $str .= "<li class='". rtrim($class) ."'>"; } else { $str .= "<li>"; } // item link element $str .= "<a href='$item->url' title='$item->title'>$item->title</a>"; // iterate possible childs // NOTE: except for homepage, since those are already included in the array // we pass to the function as first level pages (same logic added to class 'parent' name) if($item->hasChildren() && $item->id != 1) { $str .= buildNav($item->children); } // close item wrapper $str .= "</li>"; } // return ul list return "<ul>$str</ul>"; } ?> <nav class='topnav'> <?php echo buildNav($homepage->and($homepage->children)); ?> </nav> Link to comment Share on other sites More sharing options...
Christophe Posted August 6, 2015 Share Posted August 6, 2015 Hello, If you haven't done it already, and to help you during your development phase, you can activate the "debug mode" in config.php: $config->debug = true; It will help you in both the front-end and the back-end. Link to comment Share on other sites More sharing options...
kazu Posted August 7, 2015 Author Share Posted August 7, 2015 @Roope The menu works fine from post #6. Sorry, but I don't know what's a "recursive version"? Therefore, I use the version I have. Thanks for your support! Link to comment Share on other sites More sharing options...
EvanFreyer Posted August 10, 2015 Share Posted August 10, 2015 @Roope: Yeah, that one looks better, thanks for the update! @kazu: Recursive means, that a piece of code calls itself, so that it can manage the same task again and again. Regarding the navigation, this pattern ensures, that you can print the navigation for the third, fourth, n-th level as well without modifying your existing code to do that explicitly. See, if we take your code, you will only get the navigation to the second level. If somehow you want to build the third level as well, you have to add that to your code and if you want to drop the list elements and go for example for just the text and a break, you have to change your code in three places. This won't be necessary with the recursive version. If you look closely at Roope's code, you see, that it calls itself here: // iterate possible childs // NOTE: except for homepage, since those are already included in the array // we pass to the function as first level pages (same logic added to class 'parent' name) if($item->hasChildren() && $item->id != 1) { $str .= buildNav($item->children); } Here, we see, that our navigation has child elements and so we call the same function again, but with the children as parameter. If the function then finds children again, the whole thing happens again and so forth. Of course it is important to ensure, that this function terminates. Otherwise, this will be a infinite loop and not a recursive function . Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now