hook_theme and tpl.php files: registering them with drupal, and passing variables
There is one way to let drupal know about a theme function or template. There are two ways to send variables to a template, but one of them has a lot of disadvantages.
We'll use two theme functions, with an identical template setup to illustrate these methods:
Creating Theme Functions and Routing Them to a Template
There's only one way, hook_theme()
<?php
/**
* Implementation of hook_theme().
* DRUPAL WONT SEE THIS HOOK UNTIL YOU CLEAR YOUR CACHE
*/
function exampleModule_theme() {
return array(
// as in 'theme('verbose_method',...)
'verbose_method' => array(
// routes to file exampleModule/spanks-box-thing.tpl.php or [YOUR ACTIVE THEME DIRECTORY]/spanky-box-thing.tpl.php
'template' => 'spankys-box-thing',
// these variables must be called in the theme function, and will appear in the template as $title, $body, $link
'arguments' => array(
'title' => null,
'body' => null,
'link' => null,
),
),
'preprocess_method' => array(
'template' => 'spankys-box-thing',
'arguments' => array('node' => null),
),
);
}
?>
The Verbose Function Method of sending variables to the tpl.php file
The verbose method is called such because it requires you to first define all the variables in hook_theme, and you have to set all those variables yourself when you call the theme function.
<?php
$output = theme('verbose_method', $node->title, $node->teaser, l($node->title, 'node/'.$node->nid));
?>
The preprocess method of sending variables to the tpl.php file
Calling this theme function is decidedly less verbose.
<?php
$output = theme('preprocess_method', $node);
return $output;
?>
However, it requires a little extra code before it magically sends variables with nice names to the tpl.php file. Every theme function automatically looks for a preprocess function. You can preprocess in either the theme, or the module like in this example.
<?php
/**
* Note the pattern [ module_name ] _preprocess_ [ theme_hook ](&$vars)
*/
function exampleModule_preprocess_verbose_method(&$vars) {
// node is only available because we passed it when we called theme function and defined in the arguments array of hook_theme
$node = $vars['node'];
// $vars['title'] becomes $title in spanky-box.tpl.php
$vars['title'] = $node->title;
// and so on
$vars['body'] = $node->teaser;
// and so on
$vars['link'] = l($node->title, 'node/'.$node->nid);
// hint: nothing needs to be returned because of it being passed by reference (that's what the little &$ thing means in $vars)
}
?>
Discussion: Why preprocessing is superior
Generally speaking, I only use the preprocess method. Most of the time, I find myself dealing with an object, such as a $node, $user, $comment, $block, or $file. Often, I'm not exactly sure what data I'll need passed to the template when I first write it, so relying on the preprocess functions makes changes easier. Instead of having to change every instance of me having called the function, update the hook_theme function, I only have to alter a line in the preprocess function.
The preprocessing layer gives my brain a break, and doesn't require me to remember all sorts of nonsense like whether the 4th parameter of theme_gizmo is a widget, or a thingamajig. In addition, when dealing with say -- CCK nodes -- I don't have to remember $node->field_something_I_wish_named_better[0]['value'] every time I call the function.
Finally, the layer encourages better code. If I have to turn something into an item_list before it goes into the tpl.php file, I can freely take care of it in the preprocessing layer, instead of doing it before I call the theme function, or in the template file itself. All these arbitrary operations that used to go "where" now have a place. Sure the layer gets ugly at times, but at least the ugliness is contained, and I know where to look for it if I have to change it.
The only time I'll go for the verbose method is when I'm not working $objects, or other complex data types. But I find that to be a rare occurrence.
The template file
spankys-box-thing.tpl.php
<?php
print $title;
?>
<?php
print $title;
?>
<?php
print $body;
?>
<?php
print $link;
?>