Jump to content

Understanding and Reusing navRender function in the Foundation Profile


Zahari M.
 Share

Recommended Posts

Hi everyone and thanks so much for looking!

This post is perhaps a bit long winded. I do apologise. But i think the background might be important for other newbies like myself to better understand what's going on and how to setup ProcessWire... and for those of you who can help, at least you can better understand what I'm trying to do...

Ok...

The Question....

How do we create a general purpose function that we can use to lists all the child pages of a parent page?

Why we might want this...

Lets say we want to create a "section" and call this section of our website "blog"...

We start writing and create some pages:

home/blog/blog-post-1

home/blog/blog-post-2

home/blog/blog-post-3

home/blog/blog-post-4

I and my visitors can go to each of these URL's and see each of them as individual blog posts. No problems there!

What happens though when we go to to the url "home/blog"? Well, in standard form, it would just be another page and thus it would show us the contents of that page.

But what myself and many of my visitors have come to expect on this page is to instead see a "listing" of all the blog posts that we have. An index of sorts...

So we obviously need to do something a bit different here.

The solution lies in creating a new "list_blog_pages.php" template file, adding it into the system via the admin setup and associating it with the page url "home/blog".

Next we set it up in system admin such that any children of this "list_blog_pages" template file, which are effectively going to be our blog posts, will automatically use our regular article template which happens to be basic-page.php

So, whenever someone goes to the url "home/blog", the template file "list_blog_pages.php" is called up. What we need to do now is make this template file list out all the child pages i.e. our blog posts.

If we take a look inside the Standard ProcessWire Profile, we can find this bit of code located smack in the middle of our html template that we can reuse in our template file that provides us with just the functionality we need to do this...

<?php

if($page->numChildren) {
echo "<ul class='nav'>";
 foreach($page->children as $child) {
              echo "<li><p><a href='{$child->url}'>{$child->title}</a><br />
              <span class='summary'>{$child->summary}</span></p></li>";
                    }
echo "</ul>";
                }
?>

So what it does is it generates and displays our list of child pages or in this case a list of all our blog articles and their summaries.

It works just fine!

BUT....

As a personal quirk, preference or whatever you want to call it, I would like to try and remove such code from my html template. The reason for me wanting to do this is that I would prefer to minimize the mixing of logic and presentation in my theme files. Sure, just this snippet on its own isn't a lot of code. But if you want to start adding footers and conditional sidebars etc, ones template files can become very messy!

Fortunately for us, the Foundation Processwire Profile that Ryan recently created gives us an elegant solution to this problem! It does this by way of creating various  "renderNav" functions.

So... instead of inserting the block of code shown above into our template, Ryan is adding something like this instead to our template files...

$body .= renderBodyNav($page->children);

This is much cleaner! I really like this approach and would like to implement this elesewhere in my template files.

SO...

Upon studying all the various render functions that are located in _nav.php, what I can "just" about understand with my limited abilities is this.... there is a base function called renderNav.

This renderNav generates an unordered list of all child pages of a parent page. Its a very clever function in that it has a defined set of display options and renderNav can be used by other functions that enable you to change these options depending on where in the page you want to display these pages, i.e. in the main content area or the sidebars etc...

Looking at the comments in _nav.php, we have these functions....

 * renderNav(PageArray $items, [$options]) - Render a generic navigation list
 * renderSubNav(Page $page) - Render Foundation 'side-nav' list for given $page
 * renderBodyNav(PageArray $items) - Render Foundation 'side-nav' for bodycopy placement
 * renderBreadcrumbs(PageArray $items) - Render Foundation 'breadcrumbs' list
 * renderPagination(PageArray $items) - Render pagination links using Foundation classes
 * renderTopBar(PageArray $items, [array $options]) - Render Foundation 'top-bar' navigation
 * renderOrbit($images, [$width], [$height]) - Render a Foundation 'orbit' slideshow

Now, bit by bit, I am slowly seeing how this all works. It's all very efficient and clever and reuses code.

Unfortunately, for a beginner like me, its a bit of a stretch to be fully comfortable working with all these various options of functions into functions. So, what I would like to do instead is build my own function based on renderNav that I, with my limited abilities, can more readily understand :)

So here is what I came up with. I did two things....

1. Inside my "list_blog_pages.php" template file I inserted a "simplified" version of renderNav as seen below. Basically I copied renderNav, simplified it by stripping out the options array and renaming it as my function "renderBlogPosts"

function renderBlogPosts(PageArray $items) {

if(!count($items)) return '';

$out = "<ul>";
foreach($items as $item) {
$out .= "<li><a href='$item->url'>$item->title</a></li>";
$out .= "<p class='summary'>$item->summary</p>";
}
$out .= "</ul>";
return $out;
}
 

2. Inside the "basic-page.php" template that is the default article template in the ProcessWire Foundation Profile, I added this to it...

$body .= renderBlogPosts($page->children);

Guess what? It works! Yay!

So why this post?

Well, we can start getting to the actual questions now....

This is the bit I do not understand.... (PageArray $items) . I do not understand what it does and where in the documents it is mentioned.

Where I got it from was from the first line of the original function renderNav...

function renderNav(PageArray $items, array $options = array()) {

Since I wanted to simplify things by basically removing the options and instead hard code them I stripped away the array $options = array() bit from the renderNav function.

To be honest, it was just an educated guess to do that!

So dear gents...

Q1. Can anyone explain to me what (PageArray $items) is, where it comes from and what it actually does?

Q2. Is this function, for the purposes of generating a list of pages, correctly and efficiently coded?

Q3. As I havent gotten to pagination yet, how or where would I add pagination to the above?

Well... whoever read this... thanks so much for your patience!

Cheers!

Link to comment
Share on other sites

$items is a parameter of the function. When you use the function like this: renderNav($page->children), the array $page->children will be attributed to the variable $items inside the function. exactly the same as if you would do: $items = $page->children.

that function could have been written also like this:

function renderNav($items, $options) {

instead of:

function renderNav(PageArray $items, array $options = array()) {

The difference is that, in the second example (the real one), the function is forcing us to use a PageArray for $items, and a regular array for $options. Plus, in the second parameter, the function also defines a default value to $options (in this case, an empty array), so you don't have to define a second parameter when using the function.

  • Like 2
Link to comment
Share on other sites

Zahari,

Thanks for writing up about your experience,

I have quickly read your post. One thing that sometimes confuses newbies is the so-called "the ProcessWire way of doing things". You will find out that in most things, there is no such way :) Yes, you will get advice about different approaches to solving a problem. One thing that is stressed though (by the way) is to sanitize your input (this has nothing to do with your post above) - that, perhaps, is one (of the few?) examples of the "ProcessWire" way :). What am I saying? Feel free to experiment like you've done above, look at different approaches. The approach above by Ryan is one of the approaches available. When you have time, study also the default profile's template files, then look at the Blog profile and the Skyscrapers profile as well as code by other seasoned guys in these forums. You will see different approaches there. Choose what you believe makes best sense to you and ask questions. Finally, if you haven't already done so, have a read at the below link. This gem of a thread discusses various approaches to doing template files in PW:

http://processwire.com/talk/topic/740-a-different-way-of-using-templates-delegate-approach/

Happy coding! :D

  • Like 2
Link to comment
Share on other sites

Hi diogo

Thanks so much for taking a look at this :)

OK... This bit helped...

that function could have been written also like this:
function renderNav($items, $options) {

So the pattern now looks a little bit more familiar! So I have rewritten my function to look like this:

function renderBlogPosts($items) {

if(!count($items)) return '';

$out = "<ul>";
foreach($items as $item) {
$out .= "<li><a href='$item->url'>$icon$item->title</a></li> ";
$out .= "<p class='summary'>$item->summary</p>";
}
$out .= "</ul>";
return $out;
}

And of course, just like before, it is called like this in the template file:

$body .= renderBlogPosts($page->children);

So, looking at the original renderNav function and your additional comment or note of writing out $items = $page->children  as inspiration, another way to possibly make it "cleaner" would be to further hard code the function.

So... we call the function like so:

$body .= renderBlogPosts();

And our function has your "line" put into it:

function renderBlogPosts() {

$page = wire('page');
$items = $page->children

if(!count($items)) return '';

$out = "<ul>";
foreach($items as $item) {
$out .= "<li><a href='$item->url'>$icon$item->title</a></li> ";
$out .= "<p class='summary'>$item->summary</p>";
}
$out .= "</ul>";
return $out;
}

So diogo, both of these approaches work. So thanks for helping me out here with your comments. They really helped.

In your opinion diogo, out of the two approaches I have shown above, would you recommend one over the other? As I am a real newbie, I am just not able to tell if any php related code is of good quality. Thanks!

Hi kongondo

Thanks so much for reading this and your comments. Im with you about studying the blog and skyscraper profiles mate! I actually have them installed already in MAMP waiting to dig into them when time permits.

I suppose Im stuck, and very happily so into the Foundation profile at the moment as I was actually trying to integrate Foundation myself a couple of weeks before Ryan released it. But I had no idea how to integrate the Foundation menu and so I find that this particular profile is very helpful to me learning ProcessWire as it has so many solutions to the very things I hadn't a clue how to do!!

In the short time I have come to start learning and using PW, what i can see is that one of the core needs I have is to be able to generate a listing of pages for each section / parent page and that is why the creation of a good solid simple function to do just this is important to me. And one that I can understand well.

Armed with such a simple solid function, then we can copy it, rename it and edit it slightly by way of adding say an icon to each list item without having to scratch ones head by adding instances of our new additions into the existing array :rolleyes:

The link you provided me was awesome. Thanks for that kongondo! :lol:

Your timing for that link was great too kongondo!

Having seen how flexible and awesome PW is, one thing that seemed somewhat odd to me is that I cant see a readily visible way in the admin of being able to select a different php theme template file to attach to our "admin template files". These seem to be tied 1:1. From what I gather, if you want to offer different "html arrangements" such as being able to choose to render your pages fields on a 1 column page or a 3 column page, you have to create a new field and add conditional switching in your theme files.

I havent looked deeply into this. Just saying it was one strange thing to me. That thread you pointed me to looks like it's just the ticket!

Thanks so much guys!

Cheers

Link to comment
Share on other sites

In your opinion diogo, out of the two approaches I have shown above, would you recommend one over the other?

The first one is more reusable. If for some reason you would want to render the posts on another page, or simply use a different array then children, you wouldn't be able to do it with the second option. But, if you do this:

function renderBlogPosts(PageArray $items = $page->children) {
 

You can call the function with renderBlogPosts() on the original page, but be able to pass a parameter on any other page: renderBlogPosts($pages->find("template=post")) 

  • Like 2
Link to comment
Share on other sites

Can anyone explain to me what (PageArray $items) is, where it comes from and what it actually does?

The PageArray part is called "type hinting" in PHP. It is the developers way of telling PHP that it should throw an error if it's given something other than a PageArray. That way the developer doesn't have to write extra code in the function to make sure it's been dealing with the right type of info passed to it before attempting to use it.

For the consumers of the function (you and me), it's also useful just purely for readability and knowing what it expects you to give it. It's also nice to know that you'll get a consistent error immediately if you give a function the wrong info. Like if you passed in a Page rather than PageArray, you'd know right away, rather than trying to figure out for some time why the function isn't working. 

If we had a function that we wanted to accept multiple types, like say arrays, strings and ints, then of course we would not want to type hint like that. We would probably just have myfunction($something) instead. 

  • Like 3
Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...