How to Create a Custom Field Formatter in Drupal 9

As you know, Drupal has Entities (like Content Types, Blocks, Users), each Entity contains a group of fields, these fields will have a type, some types are basics like Text or Numbers and others are more complex like Links, File, Images, References to others entities and many more, depending on the installed modules.

Each Entity has displays to be represented in different places of the site. By default we will have a Default view, in Content types we will also have teaser, however, we can create new and more display modes depending on our needs. See this post to get more information about Display Modes.

On each display, we have to select which fields that will be presented to the user. Foreach field, we have to select the formatter to render the field in the display.

So, What is a formatter?. The formatter is a part of drupal that has the responsibility to format the data in the field to be presented by the user. 

Out of the box Drupal has many formatters for our fields. If we don’t have the one that we are needing, there are many contrib modules with more formatters to add. As the last resource, we can create our own custom formatter. We will see in this post how to do it.

Formatters are defined as Plugins, the Drupal Plugin API is a mechanism that Drupal has to extend and provide new functionally to Drupal in an object-oriented way. 

Let’s see how to create a custom formatter step by step

Step 1 : Create a module

Inside of your module directory create a directory with the name of your custom module. I’ll call it sample_formatter.

Inside this directory create two files sample_formatter.module and sample_formatter.info.yml

The first one is to add PHP code for the module, for hooks mostly. The second one is to define the module metadata, it will look something like this.

name: Sample Formatter
type: module
description: 'A Simple formatter to learn how this works'
core_version_requirement: ^9
dependencies:
 - field

Once you have done this, enable your module.

Step 2: Create the formatter Class

Once again, in the module directory create a directory in src/Plugin/Field/FieldFormatter

As you might be able to note:

  • src is where all object oriented code goes in Drupal. 
  • Plugin is where all plugins are created. 
  • Field is because this is a plugin for Fields 
  • FieldFormatter is where all your field formatters will be placed. 

This is a convention to organize our code and also will be used to define our namespaces. 

Create a new class file called MyCustomFormatter.php (you can change the names, be sure to change it properly in all places).

This file must contain, the namespace

namespace Drupal\sample_plugin\Plugin\Field\FieldFormatter;

The required uses

use Drupal\Core\Field\{FormatterBase, FieldItemListInterface};

FieldFormatter base is the base class for Formatters plugins, we will extend this class.

FieldItemInterface is the interface of the object that will be sent to our formatter with the field to fo format.

The plugin API works through annotations, so we will need to create the class and set the proper annotations to make it work.

/**
* Plugin implementation of the 
*    'MyCustomFormatter' formatter.
*
* @FieldFormatter(
*   id = "sample_formatter_my_custom_formatter",
*   label = @Translation("My Custom Formatter"),
*   field_types = {
*     "string_long",
*     "string"
*   }
* )
*/
class MyCustomFormatter extends FormatterBase  {

}

Important lines here.

  • @fieldFormatter is where the annotation starts.
  • id : is the internal id of the formatter, it must be unique, to avoid name collisions, add the module name and the formatter name after that, in sneak case format.
  • Label: is what the user will see in the administration form editing the display, it must be a representative name and clear for the user.
  • Field_types: defines to which field types this formatter will be available. If you don’t see your formatter for your fill there is a chance that your field type is not in the list.

Finally is the class with the name, same as the file to be discovered by the autoloader, and we will extend from FormatterBase.

FormatterBase is a class from the Drupal Core, is an abstract class that requires the implementation of the method viewElements. We have other methods to extend to get more customizations on the formatter, in this post we will not cover those methods, only viewElements.

Step 3: Implement viewElements

As we said, ViewElements is a method that needs to be implemented in the child class. 

This method receives two parameters, the FieldItemList and the langcode. 

The FieldItemList is the values of the list, remember that a field can have multiple values on drupal, that is why FieldItemList is the object that allows us to access the field values.

This method must return an render array, and remember, the fields can have multiple values, so the expected response is an array will have multiple values (at least 1).

The code will look something like this

public function viewElements(
    FieldItemListInterface $items, 
    $langcode) 
{
  $elements = [];

  foreach ($items as $delta => $item) {
    $elements[$delta] = [
      '#markup' => "Hello formatter, 
            the value is $item->value",
    ];
  }

 return $elements;
}

Step 4: Test the formatter

The code is done, I’m assuming that your module is already enabled and working at this point.

Clear all the caches.

Now go to a content type, manage display, select a string field and open the formatter drop down options, your formatter must be in the list.

That’s all, now when you render the content on that display you’ll see the output generated by that formatter. 

This is really helpful to avoid large template customization. 

Here you can customize a small part, the field, and also reuse this customization on other entities with the sample type.

This feature used in combination with Display Modes and Display Suite is the way to do flexible displays, reusable features and to have easy to maintain code. As a general rule, if you are doing a lot of template customization, you are doing it wrong, there is a better way. 

Selecting the right contrib modules to have more custom formatters or implementing the ones for your specific needs is a more accurate path to have a more flexible drupal site.

Extra Step: Improve the rendering using #theme

In this example, we are returning the #markup element. A better implementation is to return a #theme element in order to use a custom template to render variables. 

For that we will need to implement a hook_theme in the module file (sample_formatter.module), the hook will look like this

function sample_formatter_theme(
    $existing, 
    $type, 
    $theme, 
    $path) 
{
  return [
    'sample_formatter_my_custom_formatter' => [
       'variables' => [
         'value' => NULL,
       ],
  ];
}

Then, we have to create a directory in the module called templates, and inside of this directory create a template called sample-formatter-my-custom-formatter.html.twig (is the same name than the theme array key with the dash ( _ ) changed for hyphens (-) with the .html.twig extension.

This file content must be a valid twig template, something simple will be like this.

<div>
  {{ value}}
<div>

Now we have a custom template for our field, now we have to return an array of #theme and #{variables}, for our custom theme we will have to update our viewElements method like this

 foreach ($items as $delta => $item) {
   $elements[$delta] = [
     '#theme' => 'sample_formatter_my_custom_formatter',
     '#value' => $item->value,
   ];
 }

That’s all, now your field will be rendered through the template.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.