Creating Field Formatter
The field formatter module formats field data for viewing by the end user. Field formatters are defined as plugins, so it's recommended to review the Plugin API before writing a new field formatter.
Field Formatter Class
File: /modules/random/src/Plugin/Field/FieldFormatter/RandomDefaultFormatter.php
<?php namespace Drupal\random\Plugin\Field\FieldFormatter; use Drupal\Core\Field\FormatterBase; use Drupal\Core\Field\FieldItemListInterface; /** * Plugin implementation of the 'Random_default' formatter. * * @FieldFormatter( * id = "Random_default", * label = @Translation("Random text"), * field_types = { * "Random" * } * ) */ class RandomDefaultFormatter extends FormatterBase { /** * {@inheritdoc} */ public function settingsSummary() { $summary = []; $summary[] = $this->t('Displays the random string.'); return $summary; } /** * {@inheritdoc} */ public function viewElements(FieldItemListInterface $items, $langcode) { $element = []; foreach ($items as $delta => $item) { // Render each element as markup. $element[$delta] = ['#markup' => $item->value]; } return $element; } }
Formatter Settings
If your formatter requires custom display settings, you’ll need to complete three steps:
- Override PluginSettingsBase::defaultSettings() to set default values
- Create a configuration schema for the settings
- Create a form to allow users to change the settings
Step 1: Override defaultSettings()
/** * {@inheritdoc} */ public static function defaultSettings() { return [ 'text_length' => 'short', ] + parent::defaultSettings(); }
Step 2: Create a configuration schema
The schema goes in:
[MODULE ROOT]/config/schema/[MODULE_NAME].schema.yml
Describing the setting from Step 1:
field.formatter.settings.[FORMATTER ID]: type: mapping label: 'FORMATTER NAME text length' mapping: text_length: type: string label: 'Text Length'
Step 3: Create the settings form
Override FormatterBase::settingsForm():
use Drupal\Core\Form\FormStateInterface;
/** * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state) { $form['text_length'] = [ '#title' => $this->t('Text length'), '#type' => 'select', '#options' => [ 'short' => $this->t('Short'), 'long' => $this->t('Long'), ], '#default_value' => $this->getSetting('text_length'), ]; return $form; }
Using #ajax in Settings Forms
Using #ajax in settings forms is complex because settingsForm() does not reside at the form root. Here’s an example with conditional display based on another setting:
public function settingsForm(array $form, FormStateInterface $form_state) { $form['display_type'] = [ '#title' => $this->t('Display Type'), '#type' => 'select', '#options' => [ 'label' => $this->t('Label'), 'entity' => $this->t('Entity'), ], '#default_value' => $this->getSetting('display_type'), '#ajax' => [ 'wrapper' => 'private_message_thread_member_formatter_settings_wrapper', 'callback' => [$this, 'ajaxCallback'], ], ]; $form['entity_display_mode'] = [ '#prefix' => '<div id="private_message_thread_member_formatter_settings_wrapper">', '#suffix' => '</div>', ]; $field_name = $this->fieldDefinition->getItemDefinition()->getFieldDefinition()->getName(); $setting_key = 'display_type'; if ($value = $form_state->getValue(['fields', $field_name, 'settings_edit_form', 'settings', $setting_key])) { $display_type = $value; } else { $display_type = $this->getSetting('display_type'); } if ($display_type == 'entity') { $form['entity_display_mode']['#type'] = 'select'; $form['entity_display_mode']['#title'] = $this->t('View mode'); $form['entity_display_mode']['#options'] = [ 'full' => $this->t('Full'), 'teaser' => $this->t('Teaser'), ]; $form['entity_display_mode']['#default_value'] = $this->getSetting('entity_display_mode'); } else { $form['entity_display_mode']['#markup'] = ''; } return $form; }
Create an AJAX callback:
public function ajaxCallback(array $form, FormStateInterface $form_state) { $field_name = $this->fieldDefinition->getItemDefinition()->getFieldDefinition()->getName(); return $form['fields'][$field_name]['plugin']['settings_edit_form']['settings']['entity_display_mode']; }
Dependency Injection in Field Formatters
To use dependency injection:
- Implement ContainerFactoryPluginInterface
- Implement create()
- Override __construct() in FormatterBase
1) Implement the Interface
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; class MyFormatter extends FormatterBase implements ContainerFactoryPluginInterface {
2) Implement create()
This example injects the entity.manager
service:
use Symfony\Component\DependencyInjection\ContainerInterface; public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { return new static( $plugin_id, $plugin_definition, $configuration['field_definition'], $configuration['settings'], $configuration['label'], $configuration['view_mode'], $configuration['third_party_settings'], $container->get('entity.manager') ); }
3) Override __construct()
use Drupal\Core\Field\FieldDefinitionInterface; /** * The entity manager service * * @var \Drupal\Core\Entity\EntityManagerInterface */ protected $entityManager; public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings, EntityManagerInterface $entityManager) { parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings); $this->entityManager = $entityManager; }
You can now use the entity manager service anywhere in your formatter class via $this->entityManager
.
Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.