Extending the page template name space

WordPress allows you to create dedicated page templates for pages by using the page slug. E.g. if you have a page called About and the page slug of this page is about then you can create a template for this page with the file name page-about.php and the templating system will select this page over the default page.php. The only exception would be if you have “named” templates and you selected a template from the template drop down.

When you get to work on a large websites with many pages, sometimes this page slug can be limiting. E.g. you might get to work on a website with two different pages with the same title. To illustrate an example; Say you have a website about authors of books. Each author has a number of pages but each author has a Background page. We can visualise this with a little diagram:

+ JRR Tolkien
  + Background
  + Books
+ Stephen King
  + Background
  + Books

As you can see, both Tolkien and King have a background page. On our website both these pages have unique designs so we can’t capture them into a single template. One option would be to create unique page slugs for each Background page, e.g. background-tolkien and background-king, but that becomes a bit messy.

Wouldn’t be nice if we could use the page hierarchy in the URL name space so we can distinctly target the various Background pages. E.g. If the URL for Tolkien’s background page would be jrr-tolkien/background that our page template would be named page-jrr-tolkien-background.php.

Luckily we can do this by tapping into the page_template filter. The following function will do exactly this:

function my_page_template( $templates )
{
    global $wp_query;

    $templates = array();

    $current_page_template = get_post_meta( $wp_query->post->ID, '_wp_page_template', TRUE );

    if( !empty( $current_page_template ) )
    {
        $templates[] = $current_page_template;
    }

    $uri = $_SERVER['REQUEST_URI'];

    $i = strpos($uri, "?");

    if( $i !== FALSE )
    {   
        $uri = substr($uri, 0, $i );
    }

    $elements = explode("/", $uri);

    array_shift( $elements );

    while( count( $elements ) > 0 )
    {
        $route = implode("-", $elements);

        if( !empty( $route ) )
        {
            $templates[] = "page-$route.php";
        }

        array_shift( $elements );
    }

    if( is_home() OR is_front_page() )
    {
        $templates[] = "page-home.php";
    }

    $templates[] = "page.php";

    return locate_template( $templates );   
}

add_filter('page_template', 'my_page_template' );

The above code adds functionality to create more specific page templates based on the hierarchy of the page. E.g. as in the example given earlier, when the following URL is accessed jrr-tolkien/background default WordPress behavior would be to look for the following templates in the following order:

+ page-background.php
+ page.php

However, with our new functionality added, WordPress will look for the following templates in the given order:

+ page-jrr-tolkien-background.php
+ page-background.php
+ page.php

As you can see, this will make the templating system more versatile for large website where there might be a clash between templates. Of course, the above functionality does keep in mind if a specific page template was selected for them page drop-down. It if was, this page will then take the highest precedence.

Bon App├ętit!

Tagged with: