Drupal 8 Field API series part 1: field formatters

The Drupal 8 cycle has entered the API freeze since the 1st of July, which means it's time to start porting modules or simply play around with the API. While there are exceptions to change the API, you can safely assume that 95% (or even more) will remain as it is today.

This is the first article which will be part of a series of changes in Field API for Drupal 8: field formatters. In case there are updates to the API, we will update the articles, but also the change records on drupal.org. Watch out for the next couple of weeks and months and hopefully you're prepared for Drupal 8 and Field API.

Plugins

Creating field formatters in Drupal 7 was done by implementing four hooks. In Drupal 8, formatters are now plugins using the new Plugin API. Hooks are replaced by methods in classes, which means that your module file will be empty if you only provide a formatter (unless you also implement one of the field formatter alter hooks). Being classes, this means that field formatters can now extend on each other. A good example in core is the image field formatter extending the file field formatter class. Discovery and class instantiation is managed by the new formatter plugin manager.

Create a file like '{your_module}/lib/Drupal/{your_module}/Plugin/field/FieldFormatter/{NameOfYourFormatter}.php. That's a lot of directories right ? Welcome to the world of PSR-0, namespaces and plugins in D8. This is most likely going to change, feel free to read, or even help along in https://drupal.org/node/1971198. Also, plugin managers can now control where plugins reside, see https://drupal.org/node/2043379, so we'll probably change this at some point.

In most cases, you will want to extend the FormatterBase class which does most of the heavy lifting for you. Following classes will usually be imported at the top of your file:

<?php
// FormatterBase class.
Drupal\Core\Field\FormatterBase;
// FieldItemInterface
use Drupal\Core\Field\FieldItemListInterface;
?>

1. hook_field_formatter_info() are now annotations

hook_field_formatter_info() is replaced by annotation-based plugin discovery, using the \Drupal\field\Annotation\FieldFormatter annotation class. As for other plugin types, the accepted properties are documented in the annotation class. Other modules can extend this by implementing hook_field_formatter_info_alter(). In core, the edit module adds the edit property so it knows which in-place editor it has to use. Note that some property names have changed since Drupal 7 (spaces replaces by underscores). This is how an annotation looks like, which is placed right above the class keyword.

<?php
/**
* Plugin implementation of the 'foo_formatter' formatter
*
* @FieldFormatter(
*   id = "foo_formatter",
*   label = @Translation("Foo formatter"),
*   field_types = {
*     "text",
*     "text_long"
*   },
*   settings = {
*     "trim_length" = "600",
*   },
*    edit = {
*      "editor" = "form"
*    }
* )
*/
class FooFormatter extends FormatterBase { }
?>

2. hook_field_formatter_settings_form() becomes FormatterInterface::settingsForm()

Next up is to create a settingsForm() method. If you have an old settings form, you can simply move the code to this method. Settings are automatically saved and can be accessed by calling $this->getSetting('settings_key');. Remember to always start with an empty $elements array and not with the $form argument from the function arguments.

<?php
 
/**
   * {@inheritdoc}
   */
 
public function settingsForm(array $form, array &$form_state) {
   
$element = array();

   
$element['trim_length'] = array(
     
'#title' => t('Trim length'),
     
'#type' => 'number',
     
'#default_value' => $this->getSetting('trim_length'),
     
'#min' => 1,
     
'#required' => TRUE,
    );

    return
$element;
  }
?>

3. hook_field_formatter_settings_summary() becomes FormatterInterface::settingsSummary()

Settings are accessed by calling $this->getSetting('settings_key');. Another change is that the summary now needs to return an array instead of a string.

<?php
 
/**
   * {@inheritdoc}
   */
 
public function settingsSummary() {
   
$summary = array();
   
$summary[] = t('Trim length: @trim_length', array('@trim_length' => $this->getSetting('trim_length')));
    return
$summary;
  }
?>

4. hook_field_formatter_prepare_view becomes FormatterInterface::prepareView() and hook_field_formatter_view() becomes FormatterInterface::viewElements()

The first method allows you to add additional information on the items, the second is where the actual formatting happens. Settings are accessed by calling $this->getSetting('settings_key');. Also, the methods now receive the field values as a \Drupal\Core\Field\FieldItemListInterface object, rather than an $items array in Drupal 7. More information can be found about Drupal 8 Entity API and the syntax around field values in the handbook. Simply put, FieldItemListInterface objects can be accessed and iterated on like an array of items keyed by delta, and properties in each item can be accessed by simple object syntax.

<?php
 
/**
   * {@inheritdoc}
   */
 
public function viewElements(FieldItemListInterface $items) {
   
$elements = array();

   
$text_processing = $this->getSetting('text_processing');
    foreach (
$items as $delta => $item) {
      if (
$this->getPluginId() == 'text_summary_or_trimmed' && !empty($item->summary)) {
       
$output = $item->summary_processed;
      }
      else {
       
$output = $item->processed;
       
$output = text_summary($output, $text_processing ? $item->format : NULL, $this->getSetting('trim_length'));
      }
     
$elements[$delta] = array('#markup' => $output);
    }

    return
$elements;
  }
?>

Alter hooks

The alter hooks are still the same for Drupal 8, with one small API change in hook_field_formatter_settings_summary_alter() which is invoked by the Field UI module. The summary is now an array of strings instead of a single string. <br/> will be automatically inserted between the strings when the summary is displayed.

<?php
/**
* Implements hook_field_formatter_settings_summary_alter().
*/
function my_module_field_formatter_settings_summary_alter(&$summary, $context) {
 
// Append a message to the summary when an instance of foo_formatter has
  // mysetting set to TRUE for the current view mode.
 
if ($context['formatter']->getPluginId() == 'foo_formatter') {
    if (
$context['formatter']->getSetting('mysetting')) {
     
$summary[] = t('My setting enabled.');
    }
  }
}
?>

The other two hooks are hook_field_formatter_info_alter() allowing you to make changes to the formatter definitions and hook_field_formatter_settings_form_alter() which is invoked from by the Field UI module when displaying the summary of the formatter settings for a field.

Resources

Conclusion

Writing and maintaining field formatters for Drupal 8 is not hard. In most cases, when porting, it's simply moving the contents of your old hooks to the methods in a class. In the next part, we will see how you write widget plugins in Drupal 8.

Comments

Submitted by Naveen Valecha on July 22, 2013 - 14:24

Really Helpful Article.Waiting for next part

Submitted by guest on July 22, 2013 - 16:22

"Create a file like '{your_module}/lib/Drupal/{your_module}/Plugin/field/formatter/{Formatter}.php."

seriously, is this a joke? 8 levels of directory structure for a simple field formatter? only a hardcore dev could possible think this is some sort of improvement.

Submitted by Ryan on July 22, 2013 - 21:41

Honestly, I don't see what the big deal is. It's cheap to create and navigate directories in return for auto discovery and loading. It might be nice to have to create fewer directories, but as with any convention, once it's learned it doesn't really matter. We all got used to naming functions with very specific (and often obtuse) names in order to have auto discovery of alter hooks, for example.

Submitted by guest on July 22, 2013 - 22:25

it's not a matter of 'cheapness' --- the longer the convention the more error prone it is. That's just human behavior. The function naming convention never exceeds 2 or 3 levels as far as I know. That's much easier to remember and far less error prone than 8 (8!).

There are scads of maintainers that do not live and breath this stuff every minute of the day and I guarantee (even after they learn it) they, as will i, will leave off a 'lib' or 'field' or mess up the case (lets not forget, it's mixed case as well) routinely. DX -----

Submitted by guest on July 22, 2013 - 22:26

Good to know it's already been thought of as an issue-- hopefully they'll be some way I can help.

Submitted by Chris on July 22, 2013 - 17:05

For the annotation piece, this is LITERALLY discovered inside of the comments (provided it's formatted correctly with the @ sign, etc)?

Submitted by swentel on July 22, 2013 - 17:07

Yes. Instead of writing PHP in an info hook, the definition of field formatter plugins - any plugin actually in core - lives in those comments.

Submitted by guest on July 22, 2013 - 22:20

wow wow wow, code behavior being dictated by comments?? I swear I'm not trying to be negative here, but this article is seriously distressing.

Submitted by swentel on July 22, 2013 - 23:07

Note that annotations are not something we've invented for describing the metadata of plugins. They are widely used in the java world, although I'm not sure if it originated there. If you want more detailed info on annotations, there's a dedicated doc page on d.o: https://drupal.org/node/1882526.

Pro tip: a comma at the end will crash the parser (I know, don't ask).

Submitted by Nick on July 22, 2013 - 19:21

Thanks for this article, look forward to seeing more in the near future!

Submitted by Tim on July 25, 2013 - 11:45

Oh no, please, not annotations! This is a horrible approach! Annotations aren't debugabble and IDE's don't support them... Drupal, don't become Symfony, please!!!

Instead of annotations, there could be just a config function/array inside the class, this is simpler, more performant and bug prone... damn... (

Submitted by guest on August 5, 2013 - 17:59

"Drupal, don't become Symfony, please!!!"

far too late-- it's already like 1/2 way there.

Submitted by wizonesolutions on October 25, 2014 - 18:31

I'm currently reading a documentation page about Field Items, and it links to this series. It would be great to see this series as part of the "Drupal 8 APIs" book on drupal.org. Do I have your permission to re-use potentially some/all of the content for that?

Submitted by swentel on October 25, 2014 - 20:19

You absolutely have :)

Note that here and there the actual information might already be outdated, I need to update the articles against the latest API (which is now finally in a more or less stable state)

Add new comment

You are here