Field API - Creating your own field formatters
Drupal 7's Field API is amazing—it allows us to easily add fields to any type of entity, and customize those fields with various widgets and display formats. I'm going to walk you through two examples of how you can leverage the Field API to create your own custom field formats.
Example use cases:
- You're using the phone field to display phone numbers, but you'd like to customize the HTML output to make it mobile-compatible (click to call).
- Your nodes display full addresses via the addressfield module, but you'd like to render those addresses as google maps links.
Let's start at the beginning: you're going to be creating a custom module. Let's call is grasmash.module for vanity's (and sanity's?) sake.
We'll start by letting the Field API know that we have a new field format for it to play with.
/**
* Implements hook_field_formatter_info().
*/
function grasmash_field_formatter_info() {
return array(
'grasmash_phone_mobile_call_link' => array(
'label' => t('Mobile Call Link'),
'field types' => array('phone_number'),
'multiple values' => FIELD_BEHAVIOR_DEFAULT,
),
}
What exactly did we do there? We told the Field API:
For any field of type phone_number, make available a new format called grasmash_phone_mobile_call_links with the noob name "Mobile Call Link." For fields with multiple values, just do your thing.
See hook_field_formatter_info() for more details.
Next, we'll let the Field API know what it should do when a user asks to view a field in this format. This is done via hook_field_formatter_view().
Given that we may want to define additional field formatters later, we're going to write something that will work for any custom formatter. Our implementation of hook_field_formatter_view() will cycle through all of the values for a given field (if it is multi-valued), and for each delta, it will generate markup by calling a theme() function that corresponds with the requested display format.
/**
* Implements hook_field_formatter_view().
* This code just passes straight through to a theme function.
*/
function grasmash_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$elements = array();
foreach ($items as $delta => $item) {
$element = array('element' => $item, 'field' => $instance, 'display' => $display);
$elements[$delta] = array(
'#markup' => theme('grasmash_formatter_'. $display['type'], $element),
);
}
return $elements;
}
You can see that, for each field delta, this will call a theme function matching the naming convention "grasmash_formatter_FORMATTER_NAME." Currently, those theme functions exist only in our imaginations, so let's make them!
We'll start by telling the Theme API that we have a function named grasmash_formatter_FORMATTER_NAME:
/**
* Implements hook_theme().
*/
function grasmash_theme() {
return array(
'grasmash_formatter_grasmash_phone_mobile_call_link' => array(
'variables' => array('element' => NULL),
),
);
}
I'm going to take a brief moment to talk about the structure of hook_theme(), because when I first came across it, I found it confusing.
What exactly did we do there? We told the Theme API:
Whenever a function calls
theme<span style="color: #009900;">(</span><span style="color: #0000ff;">'grasmash_formatter_grasmash_phone_mobile_call_link'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$element</span><span style="color: #009900;">)</span>
, look for a corresponding function named theme_grasmash_formatter_grasmash_phone_mobile_call_link($element). Furthermore, expect that one variable name 'element' will be along for the ride.
So, let's define that theme function!
/**
* Theme function for grasmash_formatter_grasmash_phone_mobile_call_link.
*/
function theme_grasmash_formatter_grasmash_phone_mobile_call_link($element) {
return '<a class="mobile-tel" href="tel:' . $element['element']['number'] . '">Call</a>';
}
Awesome! We can now select "Mobile Call Link" as a display format for our phone fields! Now that we've got the explanations out of the way, let's run through one more quick example: we will add a custom formatter that permits us to display addressfields as Google Maps links.
Add an additional row to the hook_field_formatter_info() array:
/**
* Implements hook_field_formatter_info().
*/
function grasmash_field_formatter_info() {
return array(
'grasmash_phone_mobile_call_link' => array(
'label' => t('Mobile Call Link'),
'field types' => array('phone_number'),
'multiple values' => FIELD_BEHAVIOR_DEFAULT,
),
'grasmash_addressfield_full_inline_gmap_link' => array(
'label' => t('Full Address Inline w/ Gmap Link'),
'field types' => array('addressfield'),
'multiple values' => FIELD_BEHAVIOR_DEFAULT,
),
}
No need to worry about hook_field_formatter_view() again, since we built it to accomodate multiple formatters. So, let's add an additional row to our hook_theme() array:
/**
* Implements hook_theme().
*/
function grasmash_theme() {
return array(
'grasmash_formatter_grasmash_phone_mobile_call_link' => array(
'variables' => array('element' => NULL),
),
'grasmash_formatter_grasmash_addressfield_full_inline_gmap_link' => array(
'variables' => array('element' => NULL),
),
);
}
And lastly, let's define our theme function and build the markup:
/**
* Theme function for grasmash_formatter_grasmash_addressfield_full_inline.
*/
function theme_grasmash_formatter_grasmash_addressfield_full_inline_gmap_link($element) {
// Mimic addressfield_field_formatter_view().
$handlers = array('address' => 'address');
$context = array('mode' => 'render');
$markup = addressfield_generate($element['element'], $handlers, $context);
// Wrap markup in a link!
$options = array(
'query' => array('q' => $markup),
'attributes' => array(
'class' => array('addressfield-gmap-link'),
),
);
$output = l($text, 'https://maps.google.com/maps', $options);
return $output;
}
Have fun!
7.x,
drupal, theming, theme api, fields, field api, formatters, phone, addressfield