Home

Lullabot

Lullabot Ideas

We know stuff. We empower you to know stuff too.

On Site Drupal Training

We'll come to you! Graduate from your own on-site courses and become Drupalistas!

Theming Best Practices (Garland Gets a Cleanup)

Article by Nate HaugApril 28, 2008 - 5:20pm

Yesterday Garland got a long-overdue update: The page.tpl.php file was updated to use best practices. Now we can finally open up Garland in a workshop scenario and not have to use it as example of the bad practices within a .tpl.php file. This article applies to Drupal 6 and higher, though the theming principles apply to all versions of Drupal.

What's this about best practices? Let's compare the before and after of a few of the improvements. Each of the items below are extremely common things you can do to keep your .tpl.php files clean.

Avoiding Function Calls Directly in a .tpl.php file

The theme layer is a place where logic and markup should be clearly separated. The .tpl.php files should be used explicitly for markup, just printing out variables whenever necessary. Template.php can be used for any extensive stints of logic or anything more complicated that printing out a variable.

Before:
page.tpl.php

    <!--[if lt IE 7]>
      <?php print phptemplate_get_ie_styles(); ?>
    <![endif]-->

After:
page.tpl.php

    <!--[if lt IE 7]>
      <?php print $ie_styles ?>
    <![endif]-->

template.php

<?php
function garland_preprocess_page(&$vars) {
 
$vars['ie_styles'] = garland_get_ie_styles();
}
?>

The difference here is that all functions have been removed from page.tpl.php. Instead, we set a variable in template.php, then simply print out that variable in page.tpl.php. Note that this is in Drupal 6, in previous versions of Drupal the same thing would be done in the _phptempate_variables() function.

Using Drupal Core's Body Classes

In the new theming system for Drupal 6 and higher, the concept of "body classes" has become standardized. That is, the layout of the page is determined by classes set on the <body> tag. Some of these classes are things like "front", "logged-in", "page-node", and "sidebar-left". These classes allow CSS to control the layout or display things in a different way for logged in users.

Before:
page.tpl.php

<body<?php print phptemplate_body_class($left, $right); ?>>

Hompage HTML

<body class="sidebar-left">

template.php

<?php
function phptemplate_body_class($left, $right) {
  if (
$left != '' && $right != '') {
   
$class = 'sidebars';
  }
  else {
    if (
$left != '') {
     
$class = 'sidebar-left';
    }
    if (
$right != '') {
     
$class = 'sidebar-right';
    }
  }

  if (isset(
$class)) {
    print
' class="'. $class .'"';
  }
}
?>

After:
page.tpl.php

<body class="<?php print $body_classes ?>">

Homepage HTML

<body class="front logged-in page-node one-sidebar sidebar-left">

No additional code is needed in template.php, since Drupal core now provides this $body_classes variable for us.

Moving Logic from page.tpl.php to template.php

This example is probably the worst offender in Garland's page.tpl.php. It contained a large section of PHP code, including creating an array, imploding that array, and several function calls. Moving that to template.php keeps our page.tpl.php clean and used just for what templates are made for: markup.

Before:
page.tpl.php (yikes)

<?php
 
// Prepare header
 
$site_fields = array();
  if (
$site_name) {
   
$site_fields[] = check_plain($site_name);
  }
  if (
$site_slogan) {
   
$site_fields[] = check_plain($site_slogan);
  }
 
$site_title = implode(' ', $site_fields);
  if (
$site_fields) {
   
$site_fields[0] = '<span>'. $site_fields[0] .'</span>';
  }
 
$site_html = implode(' ', $site_fields);

  if (
$logo || $site_title) {
    print
'<h1><a href="'. check_url($front_page) .'" title="'. $site_title .'">';
    if (
$logo) {
      print
'<img src="'. check_url($logo) .'" alt="'. $site_title .'" id="logo" />';
    }
    print
$site_html .'</a></h1>';
  }
?>

After:
page.tpl.php

<?php if ($logo || $site_title): ?>
  <h1><a href=" <?php print $front_page ?>" title="<?php print $site_title ?>">
  <?php if ($logo): ?>
    <img src="<?php print $logo ?>" alt="<?php print $site_title ?>" id="logo" />
  <?php endif; ?>
  <?php print $site_html ?>
  </a></h1>
<?php endif; ?>

template.php

<?php
function garland_preprocess_page(&$vars) {
 
// Prepare header
 
$site_fields = array();
  if (!empty(
$vars['site_name'])) {
   
$site_fields[] = check_plain($vars['site_name']);
  }
  if (!empty(
$vars['site_slogan'])) {
   
$site_fields[] = check_plain($vars['site_slogan']);
  }
 
$vars['site_title'] = implode(' ', $site_fields);
  if (!empty(
$site_fields)) {
   
$site_fields[0] = '<span>'. $site_fields[0] .'</span>';
  }
 
$vars['site_html'] = implode(' ', $site_fields);
}
?>

Proper template.php Function Prefixes

Garland has always used "phptemplate_" as the prefix for all it's theme functions. Why? The "phptemplate_" prefix indicates a function owned by PHPTemplate (Drupal's default theme engine). Sure, this works, but Garland really should be using it's own theme prefix. It's consistent with the naming conventions used throughout all the rest of Drupal modules and themes.

Before:
template.php

<?php
function phptemplate_breadcrumb($breadcrumb) {
  if (!empty(
$breadcrumb)) {
    return
'<div class="breadcrumb">' . implode(' › ', $breadcrumb) . '</div>';
  }
}
?>

After:
template.php

<?php
function garland_breadcrumb($breadcrumb) {
  if (!empty(
$breadcrumb)) {
    return
'<div class="breadcrumb">' . implode(' › ', $breadcrumb) . '</div>';
  }
}
?>

Wrap Up

Often part of the problem with Drupal's templating system is that PHP allows you to too much in the theme layer. It's tempting to just slip that SQL query directly into node.tpl.php... but don't do it! The end result can be messy, hard to update themes. Keep as much logic as possible out of your .tpl.php files, instead move large chunks of code to template.php. This keeps potential bugs in one place, and your designers will appreciate having a .tpl.php file that's easy to understand.

Comments

Michael Hofmockel (not verified) on April 28, 2008 - 6:49pm

Thanks for the great write-up

It's wonderful having Lullabot on my team! My sites just got a little better.
Thanks.

Benjamin Doherty (not verified) on April 28, 2008 - 7:06pm

what about $logo but !$site_name?

The example's cleanup of the logo, site title and slogan code has a flaw. What happens if you have a logo but not a site title?

Otherwise, if a site title is required (which it doesn't need to be) then why have the first condition?

April 28, 2008 - 7:35pm Nate Haug

Good Point, Another Bug?

The cleanup patch that got in didn't change any of the logic, it just moved it from page.tpl.php to template.php. If this is indeed a bug, it's one that's been there since Drupal 5 was released. If you can create a scenario where Garland improperly prints out the logo/title, we should open a new issue on Drupal.org.

Rafael Ferreira Silva (not verified) on April 28, 2008 - 7:26pm

Very good article

Dude, this is exactlly what I was looking for write an article about best praticies on Drupal theme development for guys on Brazilian Drupal Community.

Thanks a lot!

Laura (not verified) on April 28, 2008 - 8:31pm

Nicely presented

Thanks for writing this up. A positive code improvement translated into a learning opportunity for all.

stephthegeek (not verified) on April 28, 2008 - 8:36pm

Great writeup

Thanks for taking the time to write this up. It's always great to have a reminder of the best way to do things in your themes.

Geoff Butterfield (not verified) on April 29, 2008 - 12:43am

Thanks!

Wild, I was just thinking about theme best practices and here you are with the answers -- just in time again!

cheers,

g

Chris Charlton (not verified) on April 29, 2008 - 1:21am

Great to see

Oh good, now I can tell people to actually look at Garland's code. :)

Drupal Theme Garden (not verified) on April 29, 2008 - 7:16am

Thanks for this (short but

Thanks for this (short but useful) article about Drupal theming best practice.
Didn't know that Garland has been "purified".

Phil Glatz (not verified) on April 29, 2008 - 12:02pm

preprocess hook?

When does garland_preprocess_page() get called in your example? Is this a new hook?

April 30, 2008 - 5:53am Nate Haug

Preprocess is new in Drupal 6

In Drupal 5, there's only one function for overriding variables: _phptemplate_variables(). Drupal 6's identical version of this same function is hook_preprocess(). However, because the _phptemplate_variables function tended to get very (very) large, in Drupal 6 you can now target any theme function before it get's sent to a template, so you can also do things like garland_preprocess_node(), or garland_preprocess_block(), or anything else that uses a .tpl.php file.

brenda003 (not verified) on April 29, 2008 - 12:08pm

Great!

Thanks for this, I was recently thinking about Theming Best Practices internally and this is an excellent resource.

Nick Urban (not verified) on April 29, 2008 - 1:36pm

.sidebars vs .two-sidebars

Hey Nate,

Thanks for the post. I'm working on a Garland-based theme right now, so I was able to make use of this immediately. I might mention that the class used for body in the old Garland when both sidebars are displayed is "sidebars", whereas the new class is "two-sidebars".

So if you're trying to roll these changes into a custom Garland theme, don't forget to update your style.css (and print.css) to replace ".sidebars" with ".two-sidebars"!

Cheers,

Nick

April 30, 2008 - 5:56am Nate Haug

Thanks

I should mention that this change was also included in the patch submitted to core, you can grab the patch itself from the issue on d.o.:

http://drupal.org/files/issues/garland_page_cleanup_2.patch

Tj Holowaychuk (not verified) on April 30, 2008 - 9:14am

Themer module

The themer module (D.O/project/themer) provides body class functionality which many users may find easier to use. These include path, aggregated paths, roles, locale, random, etc

guardian (not verified) on May 12, 2008 - 12:29pm

phptemplate_ prefix

why does garland use the phptemplate_ prefix ? oh why ?

also, remember http://drupal.org/node/55126 advocates its usage as well :)

<?php
 
function chameleon_item_list($items = array(), $title = NULL, $type = 'ul') {
?>
This is not actually the preferred method of overriding, however, but it is the one that always works. Most themes, however, are derived from one of the Template Engines. They can override these functions by using the name of the template engine instead of the theme name. This is preferred, because it makes it very easy to share these functions with other users who might be using a different theme. Or even share a single file of functions with several themes on the same site!

Gavin Doolan (not verified) on May 13, 2008 - 8:16am

Great tips and explanation...but can you?

Great tips and explanation but could you possibly suggest a best practice theme that we should look at? I've read about the Zen Theme but I'm not sure if its still supported for Drupal 6?

Is there a good base theme to get started with in Drupal for either version 5 or 6? I haven't built a Drupal theme yet myself, I've just read about it in the Pro Drupal Development book, I have developed theme's for various other CMS systems, just not Drupal yet.

Cheers,
Gavin

guardian (not verified) on May 13, 2008 - 10:11am

zen!

zen is definitely the way to go, and yes it's supported for D6
http://drupal.org/project/zen

Steve (not verified) on June 20, 2008 - 5:53am

For which version?

This patch is for Drupal 7 rather than 6 isn't it? Your "before" code is in Drupal 6.2.

Anonymous (not verified) on November 10, 2008 - 12:08pm

Moving Logic from page.tpl.php to template.php

NICE TUTORIAL!
Very interesting and useful informations.
This looks good! Really good tutorial include so many helpful informations!
Excellent SITE. I will refer people to your ITEMS.
Cheers

clearance london (not verified) on December 10, 2008 - 8:19am

Function Prefixes

Thanks for this article, it's great. So great that we've made it 'sticky' on The Webmaster Forums. Now we don't have to repeat ourselves, just send people to this article!

amber jewellery (not verified) on June 13, 2009 - 4:25pm

Best Practices

GOOD WORK! THANKS!

About this 'bot

Nate Haug

Nate Haug adds a dash of design to Lullabot. He received degrees in both a Fine Arts and Computer Science from Truman State University, creating the perfect bridge between the technical and aesthetic. Detail is his obsession, so if you know what you want, Nate will deliver your desire.

Nate joined Drupal development in 2005,...

more

Recent

Drupal Voices 160: Moshe Weitzman on Page Rendering in Drupal 7

Podcast 9.02.2010

Drupal Voices 159: John Albin Wilkins on Drupal 7 Theming

Podcast 9.01.2010

Drupal Voices 158: Emma Jane Hogbin on PHP for Designers

Podcast 8.31.2010

Command Line Basics: More Editing with Vi/Vim

Video 8.31.2010

Lullabot's Back to School Sale

Blog 8.30.2010

Popular

Drupal Voices 160: Moshe Weitzman on Page Rendering in Drupal 7

Podcast 9.02.2010

Photo galleries with Views Attach

Article 6.01.2009

Drupal Voices 159: John Albin Wilkins on Drupal 7 Theming

Podcast 9.01.2010

Announcing BeautyTips, a jQuery Tooltip Plugin

Article 10.20.2008

Install a Local Web Server on Ubuntu

Video 11.14.2007
 
  • Home
  • Services
  • Events
  • Ideas
  • Store

Connect the Bots:

Twitter Facebook YouTube blip.tv All Posts Newsletter
  • Ideas
  • Blog
  • Podcasts
  • Videos
  • About
  • Contact
  • Jobs
  • Services
    • Training
  • Events
    • Training Workshops
    • Other Events
    • Conferences
    • Calendar
  • Products
    • Videos
    • Books
    • Swag
  • Ideas
    • Blog
    • Podcast
    • Videos
  • About
    • Philosophy
    • Team
    • Presskit
  • Contact
    • General
    • Work Inquiries
    • Mailing List