fromAugust 2012
Column:

The Angry Themer

Naked Fields
1

Naked Fields

Welcome to The Adventures of an Angry Themer, where we will explore some of Drupal's best and worst theming problems, how to be a markup marine without breaking too much stuff, and not waste your entire day cursing at Drupal.

One thing that can make any frontend hero angry is looking at the default output from node.tpl and the beloved $content variable with all of those nested <div> tags and classes all mashed up in one big pool of markup. This is okay for a quick prototype, but let's be honest: what we really want is lean-mean-hot-symantic-html5 along with the specific markup that will help make the perfect design and make the semantic accessibility-junkies happy.

We are going to rip two fields out of the $content variable, remove the div tag and wrappers, then apply some custom markup and place them specifically as desired for this node. All that without breaking anything!

The Pseudo Markup We Want

<article>
 <div class="width2">
    $content;
 </div>
 <div class="width8">
  $field_customfield;
 </div>
 <aside>
  $field_anothercustomfield;
 </aside>
</article>

Field.tpl and Hook Theme Suggestions

When a theme needs to change, the usual place to start the markup is to copy the modulename.tpl.php file to our local theme. Then do the custom work there. (field.tpl is found in modules/field/field.tpl)

But that would result in our overwriting every field on the site, and that can be a real pain if other modules are using the default markup and classes.

DIV Chastity

A better approach is to use the built-in field.tpl suggestions list; it gives better flexibility. A field.tpl can be overwritten when we add a .tpl file that is matching the "hook theme suggestion." (See http://drupal.org/node/1089656)

Hook Theme Suggestions

  • field--field-name--content-type.tpl.php
  • field--content-type.tpl.php
  • field--field-name.tpl.php
  • field—field-type.tpl.php
  • field.tpl

By using the different tpl suggestions we will be more flexible and only target the needed field(s). But if you are (like me) a little trigger happy in customizing fields, you will end up up having loads of field--xx.tpl.php files and that will turn into another pain down the road. Another thing is that it doesn't solve our last problem: we want 100% control over the layout of the fields in the node.tpl file.

An alternate solution is to create a new theme function that will remove the wrappers and make the data completely "naked" before it is placed in the node.tpl file.

Register a New Theme Function

template.php
/**
 * Implements HOOK_theme().
 */
function THEMENAME_theme(){
  return array(
    'nomarkup' => array (
      'render element' => 'element',
     ),
  );
}

Now Drupal knows there's a new function in town — the "nomarkup" theme function — which is also added to the template.php file.

## the nomarkup theme function
function theme_nomarkup($variables) {
  $output = '';
  // Render the items.
  foreach ($variables['items'] as $delta => $item) {
    $output .=  drupal_render($item);
  }
  return $output;
}

As usual, you'll need to clear the cache to let Drupal know to pick up our theme changes.

Magic in node.tpl.php

Now we are prepared for the magic to happen in node.tpl. But first, lets imagine a field that can be placed where you want and not have to worry about Drupal's default markup. (Sweet dream, isn't it?)

It’s very simple, actually:

  • Change the #theme for the field
  • Hide the field from $content
  • Print the field where ever you want.

node.tpl.php:

<?php
//change the theme function for the field_name
$content['field_customfield']['#theme'] = "nomarkup";
//hide the field from $content
hide($content['field_customfield']);

$content['field_anothercustomfield']['#theme'] = "nomarkup";
hide($content[' field_anothercustomfield ']);
?>

<article>
 <div class="width2">
   <?php print $content; ?>
 </div>
 <div class="width8">
   <?php print render($content['field_customfield’]); ?>
 </div>
 <aside>
   <?php print render($content['field_anothercustomfield’]); ?>
 </aside>
</article>

Now it's easy to remove the markup from any field directly in the node.tpl, just by changing the [‘#theme’] call.

This is hardcoded directly into the .tpl files, which gives us super-control over the markup, though you lose a little of the flexibility of the display modes. However, unless you're using Display Suite, you can position the field at any given place in the node.tpl anyway.

Now you have lean, naked markup placed where ever you want it in the node.tpl — so no reason to be angry any more (for today).

Image: Phoebus Moreleon

Comments

Morten, although you've written a great article here (written in your customary kick-a$$ way), and although what you've described here is the "recommended best way" to get customised field output... no offence, but in my opinion this approach still sucks. Overriding field.tpl.php, and dealing with where to hide() and where to render() things out of the $content array... it's just not fun at all.

This is why I wrote the Template Field Variables Module, and blogged about why it's a cool new way to do field theming in D7 (and I've been using it for all my D7 theming for over a year now):
http://greenash.net.au/thoughts/2012/05/introducing-the-drupal-template-...

Using my module is not the official recommended way (of course not, because you need to download yet one more contrib module) - so I'm not saying people shouldn't read your article and/or that they shouldn't do field theming as you've instructed. But people should know that there are alternatives, some providing a better themer experience than others.