ryan

Simple hooks tutorial: turn a PageArray into a list of links

Recommended Posts

In this tutorial we make a simple function that becomes part of every PageArray. Once hooked to the PageArray class, you can call this function from anything returned from $pages->find(), $page->children(), or your own page references like $page->categories from the blog profile, etc. It essentially becomes a new function available to you from any PageArray anywhere in your site. 

First, lets look at what convenience the hook function adds and how we might use it. We'll call the hook function "renderLinks", but you could of course call it whatever you wanted. We call that renderLinks() function from any PageArray, and it returns a string representing that PageArray as a list of links. 

By default, this renderLinks() functions separates each page with a comma, and outputs the page's title as the anchor text. We can change that to be anything by specifying arguments to the call. The first argument is the delimiter, which defaults to a comma ", " if not specified. The second argument is the name of the field to output, which defaults to "title" if not specified.  Next are 3 examples of how this renderLinks hook function could be used. 

Usage Examples:

Example 1: render a comma-separated list of links: 
echo $pages->find("parent=/")->renderLinks(); 
Output:
<a href='/about/'>About Us</a>, 
<a href='/contact/'>Contact Us</a>,
<a href='/site-map/'>Site Map</a>
 
Example 2: render a <ul> of $categories links:
<ul>
  <li>
  <?php echo $page->categories->renderLinks('</li><li>', 'title'); ?>
  </li>
</ul>
Output:
<ul>
  <li><a href='/categories/category1/'>Category 1</a></li>
  <li><a href='/categories/category2/'>Category 2</a></li>
  <li><a href='/categories/category3/'>Category 3</a></li>
</ul>
 
Example 3: render a breadcrumb trail: 
<p class='breadcrumbs'>
  <?= $page->parents->renderLinks(' / ') ?> 
</p>
Output:
<p class='breadcrumbs'>
  <a href='/parent1/'>Parent 1</a> / 
  <a href='/parent1/parent2/'>Parent 2</a> / 
  <a href='/parent1/parent2/parent3/'>Parent 3</a>
</p>
Those examples above show some of the potential of how you might use such a function. Next is the hook function itself. In order to be available to all templates in your site, it needs to be defined somewhere consistent that is always loaded...
 
 
Where to put the hook function: 
  • If using the basic profile (that comes with ProcessWire) you could put the hook function at the top of your /site/templates/head.inc file. 
  • If using the Foundation or Skyscrapers profile, you could put the hook function in your /site/templates/_init.php file. This is the method that I use. 
  • If using something else, you could create a /site/templates/_init.php file with your hook function(s) in it, and then edit your /site/config.php to point to it with the $config->prependTemplateFile = '_init.php'; so that it is automatically loaded on every request. Note that the name "_init.php" is not required, you can name it whatever you want. 
  • You could put it in an autoload module... but that might be overkill here. 

The actual hook function: 

wire()->addHook("PageArray::renderLinks", function(HookEvent $event) {

  // first argument is the delimiter - what separates each link (default=comma)
  $delimiter = $event->arguments(0);
  if(empty($delimiter)) $delimiter = ", ";

  // second argument is the property to render from each $page (default=title)
  $property = $event->arguments(1);
  if(empty($property)) $property = "title";

  // $out contains the output this function returns
  $out = '';

  // loop through each item in the PageArray and render the links
  foreach($event->object as $page) {
    $value = $page->get($property);
    if(!strlen($value)) continue; // skip empty values
    if(strlen($out)) $out .= $delimiter; 
    if($page->viewable()) {
      // if page is viewable, make it a link
      $out .= "<a href='$page->url'>$value</a>"; 
    } else {
      // if page is not viewable, just display the value
      $out .= $value; 
    }
  }

  // populate the return value 
  $event->return = $out; 

});

If using PHP before 5.3, or using an older version of ProcessWire, you'll need to change the first line to this (below). This syntax also works with newer versions as well, but it's not as pretty as the new syntax. :)

wire()->addHook("PageArray::renderLinks", null, 'hookRenderLinks');
function hookRenderLinks(HookEvent $event) { 
  • Like 25

Share this post


Link to post
Share on other sites

Ryan, great insight. I recently wondered if one could extend the PW API, did not know it's as easy as this.

Share this post


Link to post
Share on other sites

You didn't knew this?! Where have you been? :P

No, I always saw hooks as sth. to be added before or after some actions (and I already used them for that), but not to create new methods. I have wrapped those into my own static class methods until now =) That said I have not been actively looking into module development and hooks too much so far. Well, things are clearer now.

Share this post


Link to post
Share on other sites

Then you haven't studied the HelloWorld.module that comes as example? Hmm... it was the first thing I checked out when starting looking into modules and there's all examples also adding properties and methods. It's what definately blew my mind and made me stay with PW after all :D

Edit: I don't mean to put you down or something was just wondering and I'm glad you found out now!

  • Like 3

Share this post


Link to post
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Recently Browsing   0 members

    No registered users viewing this page.