Change record status: 
Project: 
Introduced in branch: 
8.x
Introduced in version: 
8.0-ALPHA1
Description: 

As of Drupal 8.x, four new hooks are available in Field UI API:

The new hooks allow developers to extend field formatters and widgets with third-party settings elements in the Manage Display tabs provided by Field UI. A typical use-case is to extend a field formatter with a new setting, which can be used later when rendering the field, e.g. in hook_preprocess_field().

Comparable hooks were previously provided in Drupal 7.x by the Field Formatter Settings API module in Contrib.

Usage

In this example, we will add a checkbox setting to a field formatter, and retrieve the setting in hook_preprocess_field().

Drupal 7

(requires Field Formatter Settings API module)

/**
 * Implements hook_field_formatter_info_alter().
 */
function hook_field_formatter_info_alter(array &$info) {
  // Add a setting to a formatter.
  $info['foo_formatter']['settings'] += array(
    'mymodule_extra_setting' => FALSE,
  );
}

/**
 * Implements hook_field_formatter_settings_summary_alter().
 */
function hook_field_formatter_settings_summary_alter(&$summary, $context) {
  // Append a message to the summary when foo_formatter has
  // mymodule_extra_setting set to TRUE for the current view mode.

  if ($context['instance']['display'][$context['view_mode']]['type'] == 'foo_formatter') {

    $display = $context['instance']['display'][$context['view_mode']];
    $settings = $display['settings'];

    if (!empty($summary)) {
      $summary .= '<br />';
    }

    if ($settings['mymodule_extra_setting']) {
      $summary .= t('My setting enabled.');
    }
  }
}

/**
 * Implements hook_field_formatter_settings_form_alter().
 */
function hook_field_formatter_settings_form_alter(&$settings_form, $context) {
  // Add a 'mymodule_extra_setting' checkbox to the settings form
  // for 'foo_formatter' formatters.
  if ($context['instance']['display'][$context['view_mode']]['type'] == 'foo_formatter') {

    $display = $context['instance']['display'][$context['view_mode']];
    $settings = $display['settings'];

    $settings_form['mymodule_extra_setting'] = array(
      '#type' => 'checkbox',
      '#title' => t('My setting'),
      '#default_value' => $settings['mymodule_extra_setting'],
    );
  }
}

/**
 * Implements hook_preprocess_field().
 */
function hook_preprocess_field(&$variables) {
  // Provide an extra variable to the field template when the field uses
  // a formatter of type 'foo_formatter'
  if ($variables['element']['#formatter'] == 'foo_formatter') {

    $entity_type = $variables['element']['#entity_type'];
    $field_name  = $variables['element']['#field_name'];
    $bundle      = $variables['element']['#bundle'];
    $view_mode   = $variables['element']['#view_mode'];

    $formatter_settings = field_formatter_settings_get_instance_display_settings($entity_type, $field_name, $bundle, $view_mode);

    // make the setting available in the field template
    $variables['mymodule_extra_setting'] = $formatter_settings['mymodule_extra_setting'];
  }
}

Drupal 8

/**
 * Implements hook_field_formatter_settings_summary_alter().
 */
function hook_field_formatter_settings_summary_alter(&$summary, $context) {
  // Append a message to the summary when foo_formatter has
  // my_setting set to TRUE for the current view mode.
  if ($context['formatter']->getPluginId() == 'foo_formatter') {
    if ($context['formatter']->getThirdPartySetting('my_module', 'my_setting')) {
      $summary[] = t('My setting enabled.');
    }
  }
}

/**
 * Implements hook_field_formatter_third_party_settings_form().
 */
function hook_field_formatter_third_party_settings_form($plugin, $field_definition, $view_mode, $form, $form_state) {
 $element = array();
  // Add a 'my_setting' checkbox to the settings form for 'foo_formatter' field
  // formatters.
  if ($plugin->getPluginId() == 'foo_formatter') {
    $element['my_setting'] = array(
      '#type' => 'checkbox',
      '#title' => t('My setting'),
      '#default_value' => $plugin->getThirdPartySetting('my_module', 'my_setting'),
    );
  }
  return $element;
}

/**
 * Implements hook_preprocess_field().
 */
function hook_preprocess_field(&$variables) {
  // Provide an extra variable to the field template when the field uses
  // a formatter of type 'foo_formatter'
  if ($variables['element']['#formatter'] == 'foo_formatter') {

    $entity = $variables['element']['#object'];
    $view_mode = $variables['element']['#view_mode'];
    $field_name = $variables['element']['#field_name'];

    // get the field formatter settings...
    $entity_display = EntityViewDisplay::collectRenderDisplay($entity, $view_mode);
    $field_display = $entity_display->getComponent($field_name);

    // make the setting available in the field template
    $variables['my_module_my_setting'] = $field_display['third_party_settings']['my_module']['my_setting'];
  }
}

Notes

  • In Drupal 7.x, the formatter $summary variable was passed into hook_field_formatter_settings_summary_alter() as a string. It was typically necessary to add a <br> to keep the summary display tidy.

    In Drupal 8.x, the $summary variable is an array (passed by reference) so there is no need to add the <br> manually.

  • The contrib hook hook_field_formatter_settings_form_alter() is replaced by hook_field_formatter_third_party_settings_form() in Drupal 8.x.
  • In Drupal 7.x, field_formatter_settings.module provided a helper function to retrieve the field formatter display settings: field_formatter_settings_get_instance_display_settings().

    In Drupal 8.x, the field formatter settings can be retrieved using EntityViewDisplay::collectRenderDisplay() and EntityDisplayBase::getComponent().

Impacts: 
Module developers
Themers
Updates Done (doc team, etc.)
Online documentation: 
Not done
Theming guide: 
Not done
Module developer documentation: 
Not done
Examples project: 
Not done
Coder Review: 
Not done
Coder Upgrade: 
Not done
Other: 
Other updates done

Comments

alexverb’s picture

I'm porting the Field HTML Trim module to 8.x-1.x. I used to be able to use hook_field_attach_view_alter() to alter the field output but it's now deprecated. This example proposes the use of hook_preprocess_field(). But I only need to extend the text formatters from core. So I needed to unset the #pre_render property of the item's content in order for the hook_preprocess_field() to not be overridden by it.

/**
 * Implements hook_preprocess_field().
 */
function field_html_trim_preprocess_field(&$variables) {
  foreach ($variables['items'] as $delta => &$item) {
    // Unset the preRenderSummary because it runs after preprocess_field.
    if(($key = array_search('\Drupal\text\Plugin\field\FieldFormatter\TextTrimmedFormatter::preRenderSummary', $item['content']['#pre_render'])) !== FALSE) {
      unset($item['content']['#pre_render'][$key]);
    }
    $item['content']['#text'] =  _field_html_trim_get_trimmed_html($item['content']['#text'], $trim_length, $html_trim_settings['ellipsis'], $html_trim_settings['exact']);
  }
}

So my question is: is there a way to actually extend core's field formatter plugins? Instead of unsetting it's behavior which seems unnatural...

ruhaim’s picture

To get the above code for Drupal 8 to work out of the box,
The following import statements will have to be added just below the <?p h p line


use Drupal\Core\Field\FormatterInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\Entity\EntityViewDisplay;

Please change the document to reflect that,
Thanks