Different node templates depending on URL aliases

Last modified: August 26, 2009 - 22:07

Drupal 5 includes useful mechanism for providing different templates for different pages, blocks, nodes etc. For example, you can style nodes of type 'blog' simply by adding node-blog.tpl.php which will be preferred over standard node.tpl.php template. For page templates, there is even more flexibility, so you can for example create specific templates for specific nodes, see Using different page templates depending on the current path.

The list of possible "template suggestions" is also editable via the _phptemplate_variables() function. For instance, the code snippet below will create additional "suggestions" based on the the URL alias. That's much more powerful than the default method of using the internal Drupal path.

<?php
function _phptemplate_variables($hook, $vars = array()) {
  switch (
$hook) {
    case
'page':
   
     
// Add node template suggestions based on the aliased path.
      // For instance, if the current page has an alias of about/history/early,
      // we'll have templates of:
      // node_about_history_early.tpl.php
      // node_about_history.tpl.php
      // node_about.tpl.php
      // Whichever is found first is the one that will be used.
     
if (module_exists('path')) {
       
$alias = drupal_get_path_alias($_GET['q']);
        if (
$alias != $_GET['q']) {
         
$suggestions = array();
         
$template_filename = 'node';
          foreach (
explode('/', $alias) as $path_part) {
           
$template_filename = $template_filename . '_' . $path_part;
           
$suggestions[] = $template_filename;
          }
        }
       
$vars['template_files'] = $suggestions;
      }
      break;
  }
 
  return
$vars;
}
?>

As I understand it, the

zirafa - May 14, 2007 - 21:53

As I understand it, the above code allows you to theme a node as a separate page. However, sometimes you want to be able to *just* theme the node itself, and not have to render a full page tpl.php like page.tpl.php.

Using the PHP code below, Drupal will look for these template files, split by page or no page view. This gives more fine grained control over your node tpl.php files.

Page view:

1) node-[nid]-page.tpl.php
   Node by itself on a page, specific NID
2) node-[type]-page.tpl.php
   Node by itself on a page, specific type
3) node-page.tpl.php
  Node by itself on a page, default

Listing/Teaser view:

4) node-[nid].tpl.php
   Node in list/teaser, specific NID
5) node-[type].tpl.php
   Node in list/teaser, specific type
6) node.tpl.php
   Node in list/teaser, default.

*UPDATE* Earl Miles has shown this can be done much simpler than what I had previously. Revised code:

<?php
function _phptemplate_variables($hook, $vars = array()) {
  switch (
$hook) {
    case
'node':
     
// Here is the way to switch to a different node-<something> template based on node properties.
     
if ($vars['page']) {
       
// This is LIFO (Last In First Out) so put them in reverse order, i.e
        // most important last.
       
$vars['template_files'] = array('node-page', 'node-'. $vars['node']->type .'-page', 'node-'. $vars['node']->nid .'-page');
      }
      else {
       
$vars['template_files'] = array('node-'. $vars['node']->nid);
      }
      break;
  }

  return
$vars;
}
?>

even more suggestions for node templates

schnizZzla - May 23, 2007 - 13:29

I need to be able to:

- theme nodes by type
- theme nodes by original path
- theme nodes by type and original path
- theme nodes by URL alias
- theme nodes by type and URL alias

This order also represents the weight (LIFO).

This is my version of the code:

<?php
function _phptemplate_variables($hook, $vars = array()) {
  if (
$hook == 'node')
  {
   
// Additional node templates based on original path, path alias and type.
    // More specific paths have more weight. Path aliases have more weight
    // than original paths. Suggestions with node type have more weight than without.

   
$alias = $_GET['q'];
   
$suggestions = array();
   
$name_prefix = 'node';
   
$node_type = !empty($vars['node']->type) ? '-' . $vars['node']->type : '';
   
$add_path = '';

   
// generating additional node template names, based on original path
   
foreach (explode('/', $alias) as $path_part) {
     
$add_path .= !empty($path_part) ? '-' . $path_part : '';
     
$suggestions[] = $name_prefix . $add_path;
     
$suggestions[] = $name_prefix . $node_type . $add_path;
    }

   
// adding suggestions
   
$vars['template_files'] = $suggestions;

   
// using aliases?
   
if (module_exists('path')) {
     
$alias = drupal_get_path_alias($_GET['q']);

      if (
$alias != $_GET['q']) {
       
$suggestions = array();
       
$add_path = '';
       
// generating additional node  template names, based on alias path
       
foreach (explode('/', $alias) as $path_part) {
         
$add_path .= !empty($path_part) ? '-' . $path_part : '';
         
$suggestions[] = $name_prefix . $add_path;
         
$suggestions[] = $name_prefix . $node_type . $add_path;
        }
      }
     
// adding suggestions
     
$vars['template_files'] = array_merge($vars['template_files'], $suggestions);
    }
  }
  return
$vars;
}
?>

This way I am able to use the following node template name scheme:

node-[type].tpl.php (from drupal)

Adding with this script:

node-[path].tpl.php
node-[type]-[path].tpl.php
node-[url].tpl.php
node-[type]-[url].tpl.php

I am using underline (_) in node type names, never dashes (-) and I use different names for paths and node types. Otherwise it would be too confusing.

This way I have more granularity. I can make subthemes for node types and also decide to override this for other type-path/alias combinations.

Insert a

<?php
 
echo '<pre>';
 
print_r ($vars['template_files']);
  echo
'</pre>';
?>

before the final return statement to view the suggestions ("debug mode").

It's first confusing to get a 'node-node' suggestion. Logical but an uneccesary override of node.tpl.php, maybe this should be filtered out...

Less suggestions - more control :)

tema - February 15, 2008 - 21:54

Don't parse $_GET['q']! When You're calling node_view() from node-[path].tpl.php to insert another node You can get an infinite loop (the same template is used for path anchestors).

My example is for custom subtheme of Zen theme (see more...), but You can use it as listed above.
The difference in my case is a function name, $hook is already checked, all variables are preloaded.

My "name schemes":

  • node-[type].tpl.php - it's already implemented by phptemplate engine;
  • node-[nid].tpl.php - a template for single node;
  • node-[path].tpl.php - for node with [path] alias and nodes with aliases below it ([path]/*);
  • node-[path]-nofollow.tpl.php - for single node with [path] alias, anchestors are not included.

<?php
function zencustomsubtheme_preprocess_node(&$vars) {
 
// template name for current node id
 
$suggestions = array('node-'. $vars['nid']);
 
// additional node template names based on path alias
 
if (module_exists('path')) {
   
// we already can have a path alias
   
if (isset($vars['path'])) {
     
$alias = $vars['path'];
    }
    else {
     
// otherwise do standard check
     
$alias = drupal_get_path_alias('node/'. $vars['nid']);
    }
    if (
$alias != 'node/'. $vars['nid']) {
     
$add_path = '';
      foreach (
explode('/', $alias) as $path_part) {
       
$add_path .= !empty($path_part) ? $path_part : '';
       
$suggestions[] = 'node-'. $add_path;
      }
     
// adding the last one (higher priority) for this path only
      // node-some-long-path-nofollow.tpl.php (not for anchestors)
     
$suggestions[] = end($suggestions) .'-nofollow';
    }
  }
 
$vars['template_files'] = isset($vars['template_files']) ? array_merge($vars['template_files'], $suggestions) : $suggestions;
}
?>

 
 

Drupal is a registered trademark of Dries Buytaert.