29
May

Introducing the Drupal Template Field Variables module

Drupal 7's new Field API is a great feature. Unfortunately, theming an entity and its fields can be quite a daunting task. The main reason for this, is that the field variables that get passed to template files are not particularly themer-friendly. Themers are HTML markup and CSS coders; they're not PHP or Drupal coders. When themers start writing their node--page.tpl.php file, all they really want to know is: How do I output each field of this page [node type], exactly where I want, and with minimal fuss?

It is in the interests of improving the Drupal Themer Experience, therefore, that I present the Template Field Variables module. (As the project description says,) this module takes the mystery out of theming fieldable entities. For each field in an entity, it extracts the values that you actually want to output (from the infamous "massive nested arrays" that Drupal provides), and it puts those values in dead-simple variables.

What we've got

Let me tell you a story, about an enthusiastic fledgling Drupal themer. The sprightly lad has just added a new text field, called byline, to his page node type in Drupal 7. He wants to output this field at the bottom of his node--page.tpl.php file, in a blockquote tag.

Using nothing but Drupal 7 core, how does he do it?

He's got two options. His first option — the "Drupal 7 recommended" option — is to use the Render API, to hide the byline from the spot where all the node's fields get outputted by default; and to then render() it further down the page.

Well, says the budding young themer, that sure sounds easy enough. So, the themer goes and reads up on how to use the Render API, finds the example snippets of hide($content['bla']); and print render($content['bla']);, and whips up a template file:

<?php
/* My node--page.tpl.php file. It rocks. */
?>

<?php // La la la, do some funky template stuff. ?>

<?php // Don't wanna show this in the spot where Drupal vomits
      // out content by default, let's call hide(). ?>
<?php hide($content['field_byline']); ?>

<?php // Now Drupal can have a jolly good ol' spew. ?>
<?php print render($content); ?>

<?php // La la la, more funky template stuff. ?>

<?php // This is all I need in order to output the byline at the
      // bottom of the page in a blockquote, right? ?>
<blockquote><?php print render($content['field_byline']); ?></blockquote>
 

Now, let's see what page output that gives him:

<!-- La la la, this is my page output. -->

<!-- La la la, Drupal spewed out all my fields here. -->

<!-- La la... hey!! What the..?! Why has Drupal spewed out a -->
<!-- truckload of divs, and a label, that I didn't order? -->
<!-- I just want the byline, $#&%ers!! -->
<blockquote><div class="field field-name-field-byline field-type-text field-label-above"><div class="field-label">Byline:&nbsp;</div><div class="field-items"><div class="field-item even">It&#039;s hip to be about something</div></div></div></blockquote>
 

Our bright-eyed Drupal theming novice was feeling pretty happy with his handiwork so far. But now, disappointment lands. All he wants is the actual value of the byline. No div soup. No random label. He created a byline field. He saved a byline value to a node. Now he wants to output the byline, and only the byline. What more could possibly be involved, in such a simple task?

He racks his brains, searching for a solution. He's not a coder, but he's tinkered with PHP before, and he's pretty sure it's got some thingamybob that lets you cut stuff out of a string that you don't want. After a bit of googling, he finds the code snippets he needs. Ah! He exclaims. This should do the trick:

<?php // I knew I was born to be a Drupal ninja. Behold my
      // marvellous creation! ?>
<blockquote><?php print str_replace('<div class="field field-name-field-byline field-type-text field-label-above"><div class="field-label">Byline:&nbsp;</div><div class="field-items"><div class="field-item even">', '', str_replace('</div></div></div>', '', render($content['field_byline']))); ?></blockquote>
 

Now, now, Drupal veterans – don't cringe. I know you've all seen it in a real-life project. Perhaps you even wrote it yourself, once upon a time. So, don't be too quick to judge the young grasshopper harshly.

However, although the str_replace() snippet does indeed do the trick, even our newbie grasshopper recognises it for the abomination and the kitten-killer that it is, and he cannot live knowing that a git blame on line 47 of node--page.tpl.php will forever reveal the awful truth. So, he decides to read up a bit more, and he finally discovers that the recommended solution is to create your own field.tpl.php override file. So, he whips up a one-line field--field-byline.tpl.php file:

<?php print render($item); ?>
 

And, at long last, he's got the byline and just the byline outputting… and he's done it The Drupal Way!

The newbie themer begins to feel more at ease. He's happy that he's learnt how to build template files in a Drupal 7 theme, without resorting to hackery. To celebrate, he snacks on juicy cherries dipped in chocolate-flavoured custard.

But a niggling concern remains at the back of his mind. Perhaps what he's done is The Drupal Way, but he's still not convinced that it's The Right Way. It seems like a lot of work — calling hide(); in one spot, having to call print render(); (not just print) further down, having to override field.tpl.php — and all just to output a simple little byline. Is there really no one-line alternative?

Ever optimistic, the aspiring Drupal themer continues searching, until at last he discovers that it is possible to access the raw field values from a node template. And so, finally, he settles for a solution that he's more comfortable with:

<?php
/* My node--page.tpl.php file. It rocks. */
?>

<?php // La la la, do some funky template stuff. ?>

<?php // Still need hide(), unless I manually output all my node fields,
// and don't call print render($content);
// grumble grumble... ?>
<?php hide($content['field_byline']); ?>

<?php // Now Drupal can have a jolly good ol' spew. ?>
<?php print render($content); ?>

<?php // La la la, more funky template stuff. ?>

<?php // Yay - I actually got the raw byline value to output here! ?>
<blockquote><?php print check_plain($node->field_byline[$node->language][0]['value']); ?></blockquote>
 

And so the sprightly young themer goes on his merry way, and hacks up .tpl.php files happily ever after.

Why all that sucks

That's the typical journey of someone new to Drupal theming, and/or new to the Field API, who wants to customise the output of fields for an entity. It's flawed for a number of reasons:

  • We're making themers learn how to make function calls unnecessarily. It's OK to make them learn function calls if they need to do something fancy. But in the case of the Render API, they need to learn two – hide() and render() – just to output something. All they should need to know is print.
  • We're making themers understand a complex, unnecessary, and artificially constructed concept: the Render API. Themers don't care how Drupal constructs the page content, they don't care what render arrays are (or if they exist); and they shouldn't have to care.
  • We're making it unnecessarily difficult to output raw values, using the recommended theming method (i.e. using the Render API). In order to output raw values using the render API, you basically have to override field.tpl.php in the manner illustrated above. This will prove to be too advanced (or simply too much effort) for many themers, who may resort to the type of string-replacement hackery described above.
  • The only actual method of outputting the raw value directly is fraught with problems:
    • It requires a long line of code, that drills deep into nested arrays / objects before it can print the value
    • Those nested arrays / objects are hard even for experienced developers to navigate / debug, let alone newbie themers
    • It requires themers to concern themselves with field translation and with the i18n API
    • Guesswork is needed for determining the exact key that will yield the outputtable value, at the end of the nested array (usually 'value', but sometimes not, e.g. 'url' for link fields)
    • It's highly prone to security issues, as there's no way that novice themers can be expected to understand when to use 'value' vs 'safe_value', when check_plain() / filter_xss_admin() should be called, etc. (even experienced developers often misuse or omit Drupal's string output security, as anyone who's familiar with the Drupal security advisories would know)

    In a nutshell: the current system has too high a learning curve, it's unnecessarily complex, and it unnecessarily exposes themers to security risks.

    A better way

    Now let me tell you another story, about that same enthusiastic fledgling Drupal themer, who wanted to show his byline in a blockquote tag. This time, he's using Drupal 7 core, plus the Template Field Variables module.

    First, he opens up his template.php file, and adds the following:

    /**
     * Preprocessor for node.tpl.php template file.
     */
    function foobar_preprocess_node(&$vars) {
      tpl_field_vars_preprocess($vars, $vars['node'], array(
        'cleanup' => TRUE,
        'debug' => TRUE,
      ));
    }

    After doing this (and after clearing his cache), he opens up his node (of type 'page') in a browser; and because he's set 'debug' => TRUE (above), he sees this output on page load:

    $body =

    <p>There was a king who had twelve beautiful daughters. They slept in
    twelve beds all in one room; and when they went to bed, the doors were
    shut and locked up; but every morning their shoes were found to be
    quite worn through as if they had been danced in all night; and yet
    nobody could find out how it happened, or where they had been.</p>
    <p>Then the king made it known to all the land, that if any person
    could discover the secret, and find out where it was that the
    princesses danced in the night, he should have the one he liked best
    for his wife, and should be king after his ...

    $byline =

    It's hip to be about something

    And now, he has all the info he needs in order to write his new node--page.tpl.php file, which looks like this:

    <?php
    /* My node--page.tpl.php file. It rocks. */
    ?>
    
    <?php // La la la, do some funky template stuff. ?>
    
    <?php // No spewing, please, Drupal - just the body field. ?>
    <?php print $body; ?>
    
    <?php // La la la, more funky template stuff. ?>
    
    <?php // Output the byline here, pure and simple. ?>
    <blockquote><?php print $byline; ?></blockquote>
     

    He sets 'debug' => FALSE in his template.php file, he reloads the page in his browser, and… voila! He's done theming for the day.

    About the module

    The story that I've told above, describes the purpose and function of the Template Field Variables module better than a plain description can. (As the project description says,) it's a utility module for themers. Its only purpose is to make Drupal template development less painful. It has no front-end. It stores no data. It implements no hooks. In order for it to do anything, some coding is required, but only coding in your theme files.

    I've illustrated here the most basic use case of Template Field Variables, i.e. outputting simple text fields. However, the module's real power lies in its ability to let you print out the values of more complex field types, just as easily. Got an image field? Want to print out the URL of the original-size image, plus the URLs of any/all of the resized derivatives of that image… and all in one print statement? Got a date field, and want to output the 'start date' and 'end date' values with minimal fuss? Got a nodereference field, and want to output the referenced node's title within an h3 tag? Got a field with multiple values, and want to loop over those values in your template, just as easily as you output a single value? For all these use cases, Template Field Variables is your friend.

    If you never want to again see a template containing:

    <?php print $node->field_foo['und'][0]['safe_value']; ?>

    And if, from this day forward, you only ever want to see a template containing:

    <?php print $foo; ?>

    Then I really think you should take Template Field Variables for a spin. You may discover, for the first time in your life, that Drupal theming can actually be fun. And sane.

    Additional resources

Comments are closed

Comments

29
May
2012

This is the best article I ever read about WHAT Themers simply and REALLY want to do theirs job, and all the difficulties they actually met due to the complexity of Drupal theming system.

29
May
2012

It's not client side templating but +1 for making things easier!

29
May
2012

And good job on the explanation! Way to get inside folks' heads.

29
May
2012
There are also modules that allow changing field wrappers in the UI, so you can keep doing it this way:

print render($content['field_byline']);

but without so much div soup.

See http://drupal.org/project/f...

31
May
2012

Not sure what utterly fundamental thing I'm doing wrong here...

I've followed the instructions above to the letter, but when I try to print the body field, I eg get

Notice: Undefined variable: body in include() (line 8 of sitepath/node--page.tpl.php).

31
May
2012
Jeremy Epstein

@Chris: thanks for the bug report. Turns out that there was indeed an issue, the default $body field wasn't coming through, as its internal name doesn't begin with 'field_', and so it was conflicting with the 'body' key already in the preprocess $vars array.

I've now fixed this - please download version 1.1 of Template Field Variables, and it should all work for you.

02
Jun
2012

A fun read but a video is worth a gazillion words. Can you show us this in a nice high quality (visually) video including all your tra la la la boom de ahas and such, thanks.

02
Jun
2012

He opened up his node (of type 'page') in a browser and it worked as described, He opened up his user profile page (profile2 with field collections) and he did not see anything from the module.

What should he do next?

24
Jul
2012
Zach Harkey

I loved reading this article.

I can't help but think that one of the primary reasons the Drupal themer experience is so miserable is because it is almost impossible for a themer to effectively articulate the problem in the first place.

Your style of walking the reader through a typical trial and error case study is brilliant. Casting the themer in the role of the humble protagonist illustrates the underlying problems without coming off like whining, or finger pointing, or minimizing the complexity and history of effort that led the situation to its current state.

Any future article written in hopes of advocating for the themer's experience should use this format.

I can't wait to try the module.

31
Jul
2012

Great article, awesome module.

15
Oct
2012
A themer

First off had a laugh reading this. Thanks needed that.

Having said that, without knocking the article, stumbled into one of Drupal's other pet peeves I have:

Code explained on a web page will quite often NOT work.

I've read the instructions, followed them to a t, and am not getting any output. Moreover I'm completely lost. I've been reading hours on trying how to output the value of a simple field for Pete's sake, it's not rocket science.

Who are these developers building for? Please build so people CAN ACTUALLY USE IT,and still have a social life. It seems like there is a contest within Drupal development world who can write the most complex code, instead of just making it bullet proof to use. IT IS INSANE.

RTM? Which Manual? The manual is broke, because nobody, except an elite few can understand this crap. Please save that for personal projects, but this is a general project. K.I.S.S. please.

Wasted an entire sunday on this crap.

15
Oct
2012

O.k. it seems I missed some steps.

1. You have to install this module for any of this to work. Duh, Facepalm.

2. Read the readme.txt in the module. It gives an expanded explanation which answers a lot of questions. Also it is unclear what 'entity' is. Do you mean a content type with this?

Anyway gave up for now, the readme reads like hieroglyphs to me. I can build a PHP application from scratch, etc. But can't print a simple field in a template file in Drupal.

Keep up the good work though, you're on the right path.

My rant wasn't at you. Maybe it is better to ditch Drupal and write my own application. Actually keep some spare time.

16
Oct
2012
Jeremy Epstein

@Themer:

The issues you're having sound similar to what's been reported http://drupal.org/node/1705596">in this tpl_field_vars support request. Please read that thread, follow the advice that's been given there so far, and would be great if you could add your own feedback there too.

Re: what is an "entity". Please see the http://drupal.org/node/1261744">official introductory guide to Drupal entities for help. When using tpl_field_vars, an entity is a node, a user, or a taxonomy term.

Agreed, this is a component of Drupal that is broken to some extent. tpl_field_vars is an effort to fix the broken-ness a little bit. The module does what it can, but of course it's not a magic fix for every Drupal bug that ever existed. Sorry to hear that your experience with Drupal theming so far has been negative.

06
Nov
2012

Great article, specially this line: "...what he's done is The Drupal Way, but he's still not convinced that it's The Right Way." That's exactly how I feel while struggling with Drupal-themeing.

I hope things change for Drupal 8. Front-EndDev for Drupal is, as you said, unnecessarily complex.

13
Dec
2012

A link to this article should be on the FIRST page of "Theming for Drupal 101"!! Maybe more developers/themers would embrace Drupal.

Thank you so much for this. I hope to spend more time with my family because of this. :)

13
Feb
2013
Mohammed

Dear Jaza,

Thank you! Thank you! Thank you!

Excellent article explaining the pain I've endured learning how to theme Drupal.

I can't wait to try out your module! Well done!

Mohammed