extending drupal templating

One of the most common questions on Drupal's theming forums is how to create an individual layout for a specific page or node. In this tutorial I'll show you how to do just that!

Let's start off by opening template.php in your theme's folder. If you do not have this file you're probably using a theme that is not powered by the phpTemplate templating engine, and as such, this tutorial will not be of much use to you.

Rather than walking you through every line of code (which is simple enough to be understood by anyone with basic PHP knowledge), I'll just provide the good stuff and explain it beneath:

Throw the following code into template.php, integrating with existing code when appropriate.

For Drupal 5.x:

  1. function _phptemplate_variables($hook, $vars = array())
  2. {
  3. switch ($hook)
  4. {
  5. case 'page':
  6.  
  7. //code block from the Drupal handbook
  8.  
  9. //the path module is required and must be activated
  10. if(module_exists('path'))
  11. {
  12. //gets the "clean" URL of the current page
  13. $alias = drupal_get_path_alias($_GET['q']);
  14.  
  15. $suggestions = array();
  16. $template_filename = 'page';
  17. foreach(explode('/', $alias) as $path_part)
  18. {
  19. $template_filename = $template_filename.'-'.$path_part;
  20. $suggestions[] = $template_filename;
  21. }
  22.  
  23. $vars['template_files'] = $suggestions;
  24. }
  25.  
  26. break;
  27.  
  28. case 'node':
  29.  
  30. //default template suggestions for all nodes
  31. $vars['template_files'] = array();
  32. $vars['template_files'][] = 'node';
  33.  
  34. //individual node being displayed
  35. if($vars['page'])
  36. {
  37. $vars['template_files'][] = 'node-page';
  38. $vars['template_files'][] = 'node-'.$vars['node']->type.'-page';
  39. $vars['template_files'][] = 'node-'.$vars['node']->nid.'-page';
  40. }
  41. //multiple nodes being displayed on one page in either teaser
  42. //or full view
  43. else
  44. {
  45. //template suggestions for nodes in general
  46. $vars['template_files'][] = 'node-'.$vars['node']->type;
  47. $vars['template_files'][] = 'node-'.$vars['node']->nid;
  48.  
  49. //template suggestions for nodes in teaser view
  50. //more granular control
  51. if($vars['teaser'])
  52. {
  53. $vars['template_files'][] = 'node-'.$vars['node']->type.'-teaser';
  54. $vars['template_files'][] = 'node-'.$vars['node']->nid.'-teaser';
  55. }
  56. }
  57.  
  58. break;
  59.  
  60. case 'block':
  61.  
  62. $vars['template_files'] = array('block', 'block-'.$vars['block']->delta);
  63.  
  64. if($vars['block']->subject != '')
  65. {
  66. //the "cleaned-up" block title to be used for suggestion file name
  67. $subject = str_replace(" ", "-", strtolower($vars['block']->subject));
  68.  
  69. $vars['template_files'][] = 'block-'.$subject;
  70. }
  71.  
  72. break;
  73. }
  74.  
  75. return $vars;
  76. }

For Drupal 6.x:

  1. function phptemplate_preprocess_page(&$vars)
  2. {
  3. //code block from the Drupal handbook
  4.  
  5. //the path module is required and must be activated
  6. if(module_exists('path'))
  7. {
  8. //gets the "clean" URL of the current page
  9. $alias = drupal_get_path_alias($_GET['q']);
  10.  
  11. $suggestions = array();
  12. $template_filename = 'page';
  13. foreach(explode('/', $alias) as $path_part)
  14. {
  15. $template_filename = $template_filename.'-'.$path_part;
  16. $suggestions[] = $template_filename;
  17. }
  18.  
  19. $vars['template_files'] = $suggestions;
  20. }
  21. }
  22.  
  23. function phptemplate_preprocess_node(&$vars)
  24. {
  25. //default template suggestions for all nodes
  26. $vars['template_files'] = array();
  27. $vars['template_files'][] = 'node';
  28.  
  29. //individual node being displayed
  30. if($vars['page'])
  31. {
  32. $vars['template_files'][] = 'node-page';
  33. $vars['template_files'][] = 'node-'.$vars['node']->type.'-page';
  34. $vars['template_files'][] = 'node-'.$vars['node']->nid.'-page';
  35. }
  36. //multiple nodes being displayed on one page in either teaser
  37. //or full view
  38. else
  39. {
  40. //template suggestions for nodes in general
  41. $vars['template_files'][] = 'node-'.$vars['node']->type;
  42. $vars['template_files'][] = 'node-'.$vars['node']->nid;
  43.  
  44. //template suggestions for nodes in teaser view
  45. //more granular control
  46. if($vars['teaser'])
  47. {
  48. $vars['template_files'][] = 'node-'.$vars['node']->type.'-teaser';
  49. $vars['template_files'][] = 'node-'.$vars['node']->nid.'-teaser';
  50. }
  51. }
  52. }
  53.  
  54. function phptemplate_preprocess_block(&$vars)
  55. {
  56. //the "cleaned-up" block title to be used for suggestion file name
  57. $subject = str_replace(" ", "-", strtolower($vars['block']->subject));
  58.  
  59. $vars['template_files'] = array('block', 'block-'.$vars['block']->delta, 'block-'.$subject);
  60. }

Update: If you are using Drupal 6.x, you should clear the "theme registry" whenever you create new theme functions or templates by one of the following two methods:

  • The clear button located at "Administer -> Site configuration -> Performance"
  • Calling drupal_rebuild_theme_registry(). (hint: place this at the top of your template.php file while you're developing the site & remove from production)

In Drupal 5.x everything is handled by the single _phptemplate_variables() function, while in Drupal 6.x the same functionality is achieved by making separate functions for page, node and block content.

What the above code will do is make Drupal look for more template files than just the default node.tpl.php, block.tpl.php and page.tpl.php. We've added considerably more template suggestions, allowing for very granular templating.

The best way to see and understand what template files are being looked for is to print out the $vars['template_files'] array:

  1. echo '<pre>';
  2. print_r($vars['template_files']);
  3. echo '</pre>';

... either at the end of _phptemplate_variables() (for Drupal 5.x) or at the end of the phptemplate_preprocess_page() / phptemplate_preprocess_node() / phptemplate_preprocess_block() functions (for Drupal 6.x).

The output may look something like this (example for pages):

  1. Array
  2. {
  3. [0] = 'page'
  4. [1] = 'page-about_us'
  5. [2] = 'page-about_us-staff'
  6. }

In this example, when the page mysite.com/about-us/staff is loaded, Drupal will look for the following template files: page-about_us-staff.tpl.php, page-about_us.tpl.php and page.tpl.php, using the first available one.

The same technique applies to nodes and to blocks.

When theming, remember to print out $vars['template_files'] to see what template files Drupal will look for!