Working with template suggestions

Template suggestions are alternate templates based on existing .tpl.php files. These suggestions are used when a specific condition is met and a matching file exists. All layers from core, modules, theme engines and themes can provide the suggestions. You can think of them as naming hints telling the system to pick and choose based on the right circumstances. The idea is simple but it is a powerful feature providing another layer of customization.

Devel module showing template suggestions for the possible "page" templates.
devel showing suggestions

A listing of all the suggestions for core can be found in Core templates and suggestions.

These naming suggestions are set from preprocess functions. There are plenty already provided by core. If you need to extend it further, add a preprocessor for the theming hook into your template.php file. This example add suggestions on the "page" theming hook. It can be added to any hook implemented as a template.

The prefix of "drop" should be the name of your theme.

<?php
function drop_preprocess_page(&$variables) {
  global
$user;
 
 
// Add a single suggestion.
 
if (module_invoke('throttle', 'status') && isset($user->roles[1])) {
   
$variables['template_file'] = 'page-busy';
  }

 
// Add multiple suggestions.
 
if (!empty($user->roles)) {
    foreach (
$user->roles as $role) {
     
$filter = '![^abcdefghijklmnopqrstuvwxyz0-9-_]+!s';
     
$string_clean = preg_replace($filter, '-', drupal_strtolower($role));
     
$variables['template_files'][] =  'page-'. $string_clean;
    }
  }

}
?>

There are two ways to add these suggestions.

  1. The key of 'template_file' accepts a single suggestion and it takes precedence. If the condition is met and the file exists, it will be used ignoring all others.
  2. The key of 'template_files' (plural) can accept an array of suggestions. They are processed in FILO order (first in last out order). Adding to the array should be done with a general condition first, progressively getting more specific so it cascades based on specificity.

With the above example, Drupal will attempt to use a file named "page-busy.tpl.php" when the throttling threshold is met for anonymous users (anonymous role id typically set to 1). The others inform Drupal to look for templates based on the roles assigned to the current user, e.g., "page-authenticated-user.tpl.php". If none apply, the base template of "page.tpl.php" is used.

These are simply examples. You can set any context based on any data available to you.

A few notes:

  • When adding to 'template_files', add to the array. Do not reset it since the variables are passed by reference. All the suggestions set before it in core and modules would be lost.

    <?php
     
    // Do not do this:
     
    $variables['template_files'] = array('hook-suggestion');
     
     
    // Instead do this:
     
    $variables['template_files'][] = 'hook-suggestion';
    ?>

  • Prefix the suggestion with the name of the hook it is associated with. This keeps it clear and the files grouped together. It also minimizes any chance of Drupal registering the template with a different hook.
  • Use hyphens instead of underscores for consistency. The main template will never use underscores.
  • Suggestions work only when it is placed in the same directory as the base template. Templates can be placed in any sub-directory of the theme. They must be paired into the same location.
  • The theme registry does not have to be cleared for suggestions. It's only the base template that needs to be registered. Suggestions are discovered on the fly.

Allow template suggestion based on URL path and node type.

StudioTenFour - August 28, 2008 - 05:38

What I wanted to do was to have a templates based on the path. So, www.example.com/portfolio would call page-portfolio.tpl.php and page.tpl.php for the rest of the site. In addition, I wanted to specify a different template based on www.example.com/blog. Which calls the templates differently I guess because it's a node type of "blog" instead of a regular old "page"? I think?!? I'm not sure, I'm pretty new to Drupal and am just getting started.

Anyway, I learned I needed to be able to specify template based on node type in addition to path type. So, this is the script that I cobbled together from the script above and some script someone on IRC was generous to provide.

Like I said, I'm new to Drupal and PHP actually, so I'm sure there is a better way to do this. But, here it is anyways for anyone who would like to do something similar. It took me a lot of sleuthing to find all the pieces for this code! This code should go in your template.php file and your blog template should be named page-blog.tpl.php. Enjoy! =)

<?php
function phptemplate_preprocess_page(&$variables) {
global
$user;
if (
module_exists('path')) {
   
//allow template suggestions based on url paths.
   
$alias = drupal_get_path_alias(str_replace('/edit','',$_GET['q']));
    if (
$alias != $_GET['q']) {
     
$suggestions = array();
     
$template_filename = 'page';
      foreach (
explode('/', $alias) as $path_part) {
       
$template_filename = $template_filename . '-' . $path_part;
       
$suggestions[] = $template_filename;
      }
        
$alias_array explode('/', $alias);
       
$variables['template_files'] = $suggestions;
    }

   
// Add a single suggestion.
   
if (module_invoke('throttle', 'status') && isset($user->roles[1])) {
    
$variables['template_file'] = 'page-busy';
    }

   
// Add multiple suggestions.
   
if (!empty($user->roles)) {
     foreach (
$user->roles as $role) {
      
$filter = '![^abcdefghijklmnopqrstuvwxyz0-9-_]+!s';
      
$string_clean = preg_replace($filter, '-', drupal_strtolower($role));
      
$variables['template_files'][] =  'page-'. $string_clean;
     }
    if (
drupal_is_front_page()) {
        
$variables['template_file'] = 'page-front';
    }
    }
  }
}
?>

amazing!

haley.star - August 29, 2008 - 17:44

Thank you for posting that! Very helpful!

 
 

Drupal is a registered trademark of Dries Buytaert.