My arch-nemisis is overly complex logic in template.php and page.tpl.php files. It seems to me that when a drupal codebase becomes brittle and unmaintainable, the culprit is usually going to be hundreds of conditional lines of php code in a template file. You've probably seen code like this before:
THEMES/SPAGETTI_MONSTER/TEMPLATE.PHP
<?php
function spagetti_monster_page($content) {
// determine weather the page is a node view, or edit
switch(arg(0)) {
case 'node':
if (is_numeric(arg(1)) && (!arg(2) || arg(2) == 'view')) {
$body_class = 'node_view';
}
else if (arg(1) == 'add' || arg(2) == 'edit') {
$body_class = 'node_compose';
}
// f#ck it, the clients will never look at these pages....
else {
$body_class = 'node_wtf';
}
break;
case 'user':
if (is_numeric(arg(1)) && (!arg(2) || arg(2) == 'view')) {
$body_class = 'user_view';
}
//.... and so on and so on and so on.....
break;
}
}
?>The dangers of this approach are something that cannot be explained: they must be felt first hand.
Switching args in a template.php or page.tpl.php file is like doing drugs (minus the laughs): lots of people have done it, survived, and even learned from it; but its an experience that is best avoided if you can help it. So lets see how to avoid it using static variables. (warning, you'll need to use a module).
SPAGETTI_MONSTER_KILLER.MODULE
<?php
function spagetti_monster_killer_set_body_class($class = NULL) {
// set the variable to static
static $set_class;
// if a class has been given, then go ahead and set it
if ($class) {
$set_class = $class;
}
if (!$set_class) {
// return a default class
$set_class = 'random_page';
}
// now return it
return $set_class;
}
function
spagetti_monster_killer_nodeapi(&$node, $op, $teaser, $page) {
switch ($op) {
case 'view':
if ($page == TRUE) {
// indeed..... this is clearly a page view of a node -- set the template.php body class 'node_page'....
spagetti_monster_killer_set_body_class('node_page');
}
break;
}
}
function
spagetti_monster_killer_form_alter($form_id, &$form) {
if ($form['#id'] == 'node-form') {
// we are absolutely certain we are viewing a node form... gee-wiz
spagetti_monster_killer_set_body_class('node_edit');
}
}
?>Its not hard to see why this approach allows more freedom, more maintainability, more modularity, less maintaince, and less opprotunity to make a mistake. Its likely to be a more efficent too since the class just gets set when certain functions execute (such as node_view($node, FALSE, TRUE)), instead of having to walk through a long switch statement. Using this method, all you have to have in template.php is this:
<?php
function spagetti_monster_page($content) {
// simply use a null vallue so that the function returns whatever was last in memory. In otherwords, anywhere this function ran to generate a page will determine the value. Give static a try....
$body_class = spagetti_monster_killer_set_body_class(NULL);
}
?>A final note that these examples scratch the surface of this concept. Many people will perhaps shun such an approach -- I'd like to hear their reasons. I am, after all, an idiot.
Comments
Hi, This looks like a great
Hi,
This looks like a great code but I'm searching the Net for answers because I'm not that good at PHP yet.
Can you please give some more examples of ways to target specific pages?
I need to add a classname to the body when a view called 'services' is being viewed.
Thanks!
Similar Approach
I was just recently shown a similar approach of using a static to simplify my template.php code. While this function still requires looking at args() is does simply my other logic.
<?php
function circ_get_node_type() {
static $type;
if (!$type) {
if (arg(0) == 'node' && is_numeric(arg(1))) {
$node = node_load(arg(1));
$type = $node->type;
}
else
$type = '';
}
return $type;
}
?>
Then in other template.php theme overrides and what not.
<?phpfunction circ_breadcrumb($breadcrumb) {
if (circ_get_node_type() == 'file' || circadia_get_node_type() == 'folder')
$variables = array('breadcrumb' => implode('/', $breadcrumb));
else
$variables = array('breadcrumb' => implode(' :: ', $breadcrumb));
return _phptemplate_callback('breadcrumb', $variables, array('breadcrumb'));
}
?>
I see good uses for your approach as well.
A patch for core in 6.0
I have a core patch for Drupal 6 to add this kind of feature so that a lot of the grunt work of doing this will be automagic.
It doesn't, however, have a great deal of traction yet. Maybe you can help!
http://drupal.org/node/113382
Some minor notes. You want
Some minor notes. You want if (isset($class)) the default value can be set as static $stored_class = 'default'; and I usually prefer to have a get function function spagetti_monster_killer_set_body_class() {return spagetti_monster_killer_set_body_class();}
If you change the setter later then the you do not need to change the get. It helps.
Otherwise yes, you shall do this.
Post new comment