Complete and flexible blogging platform for ProcessWire

Render Navigation

The method renderNav() is one of the more versatile methods in MarkupBlog that you are most likely to use than any other. You can use it to render various single-level secondary navigation on your Blog to list, for example, your Blog's Categories, Tags, Authors, Blog Related items, custom links, etc., in a sidebar or any other position on your Blog pages. 

<?php
/**
 * Render a secondary navigation.
 *
 * When the $mobile option is set, make the ul.nav disappear when at mobile width 
 * and instead show only the form <select> navigation instead. 
 * This happens because the css media query recognizes the 'no-mobile' class 
 * and hides any thing carrying that class. Likewise, anything with the 'mobile'
 * class is only shown when at mobile width.
 *
 * @access public
 * @param string $headline Headline to display above nav
 * @param array|PageArray $items May be an array of Page objects or array of ($url => $label)
 * @param Page|string $currentURL Current item that should be highlighted, may be Page or $url
 * @param bool $mobile Replace the nav with a <select> when at mobile width?
 * @return $out string
 *
 */
 public function renderNav($headline, $items, $currentURL = '', $mobile = true)

This method accepts four arguments, $headline, $items, $currentURL and $mobile.

The first argument $headline is used to display a headline above the navigation. If provided, it has to be a string. Otherwise, if no headline is desired, indicate this with an empty string.

The second argument $items is required for a navigation to be displayed. This argument must either be an array of ProcessWire Pages or an array of key=>value pairs corresponding to $url => $label. For instance, a URL and its Label/Title/Text/ such as 'http://processwire.com/' => 'ProcessWire'. If using an array of Pages, the various array manipulation methods available in ProcessWire WireArray such us $a->prepend($item) can be used to work on the array before passing it as the second argument $items to renderNav(). I'll show you examples later in the lesson.

The third argument $currentURL, if specified, will (via CSS) indicate the current navigation item that should be highlighted. In essence, which navigation item is 'active'. The default value is an empty string (i.e. no current URL specified). If you want to use the argument, the value you pass it must either be a Page or a $url string.

The fourth argument of renderNav() is $mobile. It requires a boolean value. It defaults to true. When used (i.e. value = true), it ensures (via CSS and Javascript) that on smaller devices a suitable <form> select secondary navigation is visible rather than the normal secondary one for larger screens.

Please note that renderNav() can be called several times on one page to output several navigation. OK, let's see some examples.

Passing $items as a $url => $label array

In this example we use renderNav() to output external inks by passing it an array of url/label key=>value pairs. We have also set a custom headline for the first argument $headline. The second argument is our array with some custom url=>label  pairs. Since our links are external, we do not have a current URL hence pass a blank string as the value of $currentURL. Although this is the default, we need to specify it here since we will be specifying the fourth argument $mobile. For the fourth argument, we do not wish to have a mobile version of the navigation so we set the value to false. That's it!

<?php
//first we call MarkupBlog ONLY IF WE HAVEN'T ALREADY
$blog = $modules->get('MarkupBlog');

//custom array with $url => $label pairs
$customUrls = array(
'http://processwire.com/' => 'ProcesWire',
'https://processwire.com/talk/' => 'ProcesWire Forums',
'http://cheatsheet.processwire.com/' => 'ProcessWire Cheatsheet',
'http://modules.processwire.com/'=> 'ProcessWire Modules',
'http://processwire.com/blog/' => 'ProcessWire News and Updates Blog',
);

//custom headline, custom url=>label pairs, no current url since external, mobile=false
echo $blog->renderNav('ProcesWire Resources', $customUrls, '', false);

The above will output an unordered list of navigation links that we can easily style using CSS (more on the markup below).

Passing $items as an array of Page objects

renderNav() can also accept an array of Page objects as its second argument $items. This is quite easy to do as shown below. In this example, we first find some pages and pass these to renderNav() as the second argument $items. This example also illustrates the point that we do not need to use renderNav() with Blog pages only. In this case, we grab documents that use the template 'basic-page' and output them as an unordered list. The $currentURL will be the page being viewed whilst we have left out the $headline.

<?php
//first we call MarkupBlog ONLY IF WE HAVEN'T ALREADY
$blog = $modules->get('MarkupBlog');

$urls = $pages->find('template=basic-page, limit=20');

echo $blog->renderNav('', $urls, $page, false);

Let's look at another example. Here, we first modify a WireArray before passing it to renderNav() to output a navigation. We prepend a link to our site's homepage to the navigation. Our highlighted link will be the page in the navigation that we are currently viewing.

<?php
//first we call MarkupBlog ONLY IF WE HAVEN'T ALREADY
$blog = $modules->get('MarkupBlog');

//We find Pages that will be part of our navigation ($items)
$blogHome = $pages->get("/blog/");

$urls = $blogHome->children('name!=posts');//we exclude the 'posts' page

//prepend our homepage to the navigation (we use the WireArray method prepend())
$home = $pages->get("/");
$urls->prepend($home);

//render the navigation $items=$urls [a PageArray]
echo $blog->renderNav('', $urls, $page);

What if we wanted to output a navigation showing our most popular (based on number of comments) Blog Posts? This is very easy to do as shown below. Note that our $headline = 'Most Popular'. 

<?php
//first we call MarkupBlog ONLY IF WE HAVEN'T ALREADY
$blog = $modules->get('MarkupBlog');

//note: sort and limit have to come after the 1st find in that order.
//We first sort by number of comments, then the post title
$urls = $pages->find('template=blog-post, blog_comments.count>0')->sort('-blog_comments.count, title')->find('limit=5');

//render the navigtion $items=$urls [a PageArray]
echo $blog->renderNav('Most Popular', $urls);

Let's take the above example a step further. Say we wanted to show the number of comments next to the title of the Blog Post, we could do it as shown below. Note, technically, we are using a combination of passing the second argument of renderNav() $items as a customised ($url=>$label) array made up of a PageArray. First, we grab our items of interest as was shown in the previous example, i.e. our most popular content. We then create a subsequent array of $url=>$label pairs. We do this because we want to append the number of comments for each returned popular Blog Post. Finally, we append the number of comments to each $label and pass this to renderNav(). This will produce an unordered list of links such as 'My Link Label (23)' where '23' is the number of comments on that particular Post.

<?php
//first we call MarkupBlog ONLY IF WE HAVEN'T ALREADY
$blog = $modules->get('MarkupBlog');

//note: sort and limit have to come after the 1st find in that order.
//We first sort by number of comments, then the post title
$items = $pages->find('template=blog-post, blog_comments.count>0')->sort('-blog_comments.count, title')->find('limit=5');

//we create and populate an array with customised $url=>$label pairs
//we also append comment counts to our $labels
$customItems = array();

foreach ($items as $item) {

    $customItems[$item->url] = $item->title . ' (' . count($item->blog_comments) . ')';
}

//render the navigation based on $customItems
echo $blog->renderNav('Most Popular', $customItems);

$currentURL

From the above examples, we've already seen how to pass $currentURL as a Page. In the following example, we pass this third argument as a custom $url string made up of a ProcessWire URL segment. The code to build up the URL segment may be unfamaliar to some but that topic is not the focus of this lesson so I will not be dwelling on it. The code below will output links similar to '/blog/authors/john/' where '/john/' is the first URL segment for the page '/authors/'. It is also the 'name' of an existing Blog Author. '/authors/' is the page where the code was placed.

<?php
//call the module
$blog = $modules->get('MarkupBlog');

$navigation = '';

//first we find blog authors
$authorRole = $roles->get('blog-author');
$superuserRole = $roles->get('superuser');
$authors = $users->find("roles=$authorRole|$superuserRole, sort=title"); 

//we'll use this to store urls to custom author pages
$authorLinks = array();

foreach($authors as $a) {
            //we set a separate URL (url2) to reflect the public url of the author, since
            //the author's $author->url is actually a page in the admin which won't display in the frontend anyway
            $a->url2 = $page->url . $a->name . '/';
            $authorLinks[$a->url2] = $a->get('title|name');
}

//if a url segment corresponding to a blog author name was input
if($input->urlSegment1) {
    //author specified: render navigation links

            $name = $sanitizer->pageName($input->urlSegment1);
            $author = $users->get($name);
            if(!$author->id || (!$author->hasRole($authorRole) && !$author->isSuperuser())) throw new Wire404Exception();

            $authorName = $author->get('title|name'); 

            //output subnav if viewing a single author's page
            $navigation = '<div id="sub-nav">' . $blog->renderNav($page->title, $authorLinks, $page->url . $author->name . '/', false) . '</div><!-- #nav -->';
            
            echo $navigation;

}

The above examples are enough to get you going. Let's look at the simple HTML output produced by renderNav() next.

renderNav() HTML output

This is the HTML output produced by renderNav(). It is pretty straightforward and needs no elaboration.

<!--navigation -->
<ul class="nav links no-mobile">
 <li><a href="/pwtests/">Home</a></li>
 <li><a href="/pwtests/blog/categories/">Categories</a></li>
 <!-- class='on' only output if the 3rd argument of renderNav() $currentURL was provided -->
 <li class="on"><a class="on" href="/pwtests/blog/tags/">Tags</a></li>
 <li><a href="/pwtests/blog/comments/">Comments</a></li>
 <li><a href="/pwtests/blog/authors/">Authors</a></li>
 <li><a href="/pwtests/blog/archives/">Archives</a></li>
</ul>

<!-- this form will only be output if the 4th argument of renderNav() $mobile
 has been left to the default value = true
 -->
<form class="mobile">
 <select class="nav">
  <option value="./"></option>
  <option value="/pwtests/">Home</option>
  <option value="/pwtests/blog/categories/">Categories</option>
  <option value="/pwtests/blog/tags/">Tags</option>
  <option value="/pwtests/blog/comments/">Comments</option>
  <option value="/pwtests/blog/authors/">Authors</option>
  <option value="/pwtests/blog/archives/">Archives</option>
 </select>
</form>

renderNav() CSS

Below are the CSS attributes in the HTML output of renderNav().

h4.nav-headline {}
ul.nav, ul.links, ul.no-mobile {}
li.on {} /* only output if $currentURL argument was specified */
a.on {} /* only output if $currentURL argument was specified */
form.mobile {} /* only output if $mobile argument = true */
select.nav {} /* only output if $mobile argument = true */

Summary

That's it for this lesson. We looked at renderNav($headline, $items, $currentURL = '', $mobile = true), a very flexible method for outputting one-level navigation links. The method's use is not limited to your Blog pages only and can be used to produce various navigation such as 'Related Categories', 'Most Popular Content', 'List of Blog Authors', to name only a few.