Rendering Drupal 7 fields (the right way)
The short answer.
The long answer.
Drupal 7 brought us Entities, and with them the powerful Field API for 'storing, loading, editing, and rendering field data.' attached to them. If you're managing everything through 'manage fields' and 'manage display' tabs of your content type, then every part of that process is rather wonderfully taken care of for you.
We often, however, come across the need to Drupal render a field outside the context of it's entity. A common example might include rendering a node's author in a sidebar block. Sure, modules like Panels and CCK Blocks will do this for you, but doing it manually is actually not that hard.
You may well have seen (or written!) code that looks something like this:
// This is a WRONG example. Don't do this. $block['content'] = $node->field_name['und']['safe_value'];
Poking around the node object for the value you wanted to print was fairly common in Drupal 6, and the 'safe_value' sounds like it's been sanitised, right? What's wrong with that? Oh, Let me count the ways.
- Firstly, the
['und']element is part of the field localisation in Drupal 7 (see this article from Gábor Hojtsy for more on that); directly accessing that value will cause issues in any kind of multi-lingual environment. Boo.
- By accessing the field value directly you miss out on any theming that might come courtesy of the normal field markup.
[safe_value]explicitly accesses the first value of the field - if you wanted every value from a multi-value field you'd need to do some sort of loop.
- Some fields (such as node references) won't have a safe_value element, only a value - which can easily be printed without thought for sanitisation. This is dangerous, not because node reference fields contain dangerous data (they're just a nid), but because it's not a helpful habit to get into, especially for new developers. Other fields types 'value' may well be highly dangerous.
Happily, field_view_field() comes the rescue!
$output = field_view_field('node', $node, 'field_name');
That will return the sanitised (fixing #4), rendered field in its entirety (fixing #3), in the current language (fixing #1) and in render-array form, so you'll get all the associated markup (fixing #2). Win! It will use the sites language fallback logic if the field has no values available for the current language. You can also pass an array of formatter options or specify a view mode as an optional argument. Nice. And doesn't it just feel better?
Bonus extra tips
Suppose you do just want a single value, and none of 'that crufty field markup' anyway? Well there are two api functions to help you out here. Firstly, we have field_get_items(): which returns a field items in the right language (again, you can specify a language if you need). You can poke around in there and get at the raw data, perhaps for using in some server-side logic or if you want to do something exotic like printing an image field's alt text somewhere else. Do be aware though, that you're dealing with raw user input here and will need to do the proper sanitisation before using these values, else you risk introducing security vulnerabilities.
Better yet, there's another handy field API function, field_view_value(), for when you want to render a single field value, which has all the right sanitisation built right in. It also uses field formatters, which is lovely. It takes a single field value taken from the array returned by field_view_value(). Here's an example:
$node = node_load($nid); $field = field_get_items('node', $node, 'field_name'); $output = field_view_value('node', $node, 'field_name', $field[$delta]);
$delta is the field value you want. On a single value field this would be 0. Simples.
And just for fun, here's a final example of passing in some formatter options to specify an imagecache preset, and make the field link to the node. This would work the same for both field_view_value() and field_view_field().
$node = node_load($nid); $image = field_get_items('node', $node, 'field_image'); $output = field_view_value('node', $node, 'field_image', $image, array( 'type' => 'image', 'settings' => array( 'image_style' => 'thumbnail', 'image_link' => 'content', ), ));
There you have it. Certainly for the example use case of the 'field-as-a-block' the Field API makes this really nice, clean and simple. We're dealing with render arrays, so it's all lovely and alterable right up to point
render() is called in a template file. And using field_view_field() and field_view_value() for rendering output also means that proper sanitisation is done for you. Brilliant.