Different node templates depending on URL aliases
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
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
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
<?phpecho '<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 :)
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":
<?phpfunction 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;
}
?>