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: </div><div class="field-items"><div class="field-item even">It'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: </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()
andrender()
– just to output something. All they should need to know isprint
. - 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'
, whencheck_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)
- It requires a long line of code, that drills deep into nested arrays / objects before it can
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.