Jump to content
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

  • Like 1

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

Making my semi-monthly attempt to get into hooks 🤪

I tried adding Ryan's renderLinks to ready.php at one of my sites... and then tried various versions of the usage, like:

echo $pages->find("parent=/")->renderLinks(); 

in the templates.  I take it as a sign of progress that I am past the 500 server error, but the output I get is the single word "array"

this is not urgent of course, but wondering if someone can point me in the right direction?

 

10639939_ScreenShot2020-02-17at3_37_33PM.thumb.png.66256676078542c303e69d47854f24ec.png

 

 

Share this post


Link to post
Share on other sites

@hollyvalero

I tried it as well (hook is also in site/ready.php), and everything works just fine. Just copy-and-pasted the hook, and used the list example, and adjusted the selector. It even works within a partial I load via include_once() and cache the output with MarkupCache 🙂

Do you run an ancient PHP version, by any chance? See Ryan's note about PHP < 5.3

Also, did you find any hints with Tracy Debugger?

Share this post


Link to post
Share on other sites
16 minutes ago, dragan said:

@hollyvalero

I tried it as well (hook is also in site/ready.php), and everything works just fine. Just copy-and-pasted the hook, and used the list example, and adjusted the selector. It even works within a partial I load via include_once() and cache the output with MarkupCache 🙂

Do you run an ancient PHP version, by any chance? See Ryan's note about PHP < 5.3

Also, did you find any hints with Tracy Debugger?

Well, that explains it.  I had ready.php in the templates folder, not up a level in the /site/ folder.  I figured it had to be something stupid and I was right 😂

At least now I can get into hooks more... thank you!

 

 

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.

×
×
  • Create New...