Home >Backend Development >PHP Tutorial >Creating Custom Field Formatters in Drupal 8
With the introduction of annotated plugins, a lot has changed in Drupal 8. We have a more streamlined approach to describing and discovering pieces of functionality that extend the core. Along with many other components, the former Field API (part of the larger and consolidated Entity API) is now based on plugins.
In this tutorial we will go through defining a custom field formatter for an existing field (image). What we want to achieve is to make it possible to display an image with a small caption below it. This caption will be the title value assigned to the image if one exists.
The code we write here can be found in this repository as the image_title_caption module. But let’s see how we can get to that final result.
Let us start by creating a new custom module (image_title_caption) with only one file:
image_title_caption.info.yml:
name: Image title caption type: module description: Uses the image title field as a caption core: 8.x dependencies: - image
Nothing out of the ordinary here. We can even enable the module already if we want.
In Drupal 8, field formatters (like field types and widgets themselves) are plugins. Core ones are defined either by core modules or can be found inside the DrupalCoreFieldPluginFieldFieldFormatter namespace. And like we’ve seen in a previous article in which we looked at custom blocks, plugins go inside the src/Plugin/ folder of our module. In the case of field formatters, this will be src/Plugin/Field/FieldFormatter directory.
Below you can see our own formatter class:
src/Plugin/Field/FieldFormatter/ImageTitleCaption.php:
name: Image title caption type: module description: Uses the image title field as a caption core: 8.x dependencies: - image
This is our plugin. Nothing else to it. Above the class declaration we have the @FieldFormatter annotation through which the plugin gets discovered. We specify a plugin ID (image_title_caption), label and an array of field types this formatter can be used with. In our case, the latter only contains the default image field type but we could support more if we wanted to, even custom field types. The values that go in that array are plugin IDs so if you look at the image field type plugin, you’ll see that its ID is image.
The class looks simple because we are extending from the default ImageFormatter plugin defined by the core Image module. For our purpose, all we need to override is the viewElements() method which is responsible for returning a render array of our field data. The latter can be found inside the $items list and can be used and prepared for rendering.
The first thing we do in this method is make sure we call the parent class method on the items and store that in a variable. That will already prepare the image to be rendered just as if it would normally.
By default, the ImageFormatter plugin (the parent) uses the image_formatter theme inside the render array to output the image field values. What we do here is that for each item, we replace this theme with our own: image_title_caption_formatter. Then we return the elements (render array) just like the parent does.
You’ll notice this a lot in Drupal 8: we get a very good indication on what we need to do from the parent classes we extend. And if you ask me, that is much better than figuring out what some magic hook or function does.
Since the image_title_caption_formatter theme we specified above is so far imaginary, we’ll need to create it. Inside the .module file of our module we need to implement hook_theme:
image_title_caption.module:
<span><span><?php </span></span><span> </span><span><span>/** </span></span><span><span> * <span>@file </span></span></span><span><span> * Contains \Drupal\image_title_caption\Plugin\Field\FieldFormatter\ImageTitleCaption. </span></span><span><span> */ </span></span><span> </span><span><span>namespace Drupal<span>\image_title_caption\Plugin\Field\FieldFormatter</span>; </span></span><span> </span><span><span>use Drupal<span>\Core\Field\FieldItemListInterface</span>; </span></span><span><span>use Drupal<span>\image\Plugin\Field\FieldFormatter\ImageFormatter</span>; </span></span><span> </span><span><span>/** </span></span><span><span> * Plugin implementation of the 'image_title_caption' formatter. </span></span><span><span> * </span></span><span><span> * @FieldFormatter( </span></span><span><span> * id = "image_title_caption", </span></span><span><span> * label = @Translation("Image with caption from title"), </span></span><span><span> * field_types = <span>{ </span></span></span><span><span> * "image" </span></span><span><span> * <span>} </span></span></span><span><span> * ) </span></span><span><span> */ </span></span><span><span>class ImageTitleCaption extends ImageFormatter { </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function viewElements(FieldItemListInterface $items) { </span></span><span> <span>$elements = <span>parent::</span>viewElements($items); </span></span><span> <span>foreach ($elements as &$element) { </span></span><span> <span>$element['#theme'] = 'image_title_caption_formatter'; </span></span><span> <span>} </span></span><span> </span><span> <span>return $elements; </span></span><span> <span>} </span></span><span> </span><span><span>}</span></span>
This should look familiar as it is very similar to Drupal 7. Please take note of the variables we pass to this theme. We intend to override the default image_formatter theme so we should have the same variables passed here as well. Additionally, since the image_formatter theme is preprocessed, we’ll need to create a preprocessor for our theme as well:
<span>/** </span><span> * Implements hook_theme(). </span><span> */ </span><span>function image_title_caption_theme() { </span> <span>return array( </span> <span>'image_title_caption_formatter' => array( </span> <span>'variables' => array('item' => NULL, 'item_attributes' => NULL, 'url' => NULL, 'image_style' => NULL), </span> <span>), </span> <span>); </span><span>}</span>
In this preprocessor we perform two actions:
For sanitisation, we use the helper String class statically. We are still inside the .module file so we cannot inject it, but we need to use it at the top of the file:
name: Image title caption type: module description: Uses the image title field as a caption core: 8.x dependencies: - image
Lastly, we need to create a template file for our new theme:
templates/image-title-caption-formatter.html.twig:
<span><span><?php </span></span><span> </span><span><span>/** </span></span><span><span> * <span>@file </span></span></span><span><span> * Contains \Drupal\image_title_caption\Plugin\Field\FieldFormatter\ImageTitleCaption. </span></span><span><span> */ </span></span><span> </span><span><span>namespace Drupal<span>\image_title_caption\Plugin\Field\FieldFormatter</span>; </span></span><span> </span><span><span>use Drupal<span>\Core\Field\FieldItemListInterface</span>; </span></span><span><span>use Drupal<span>\image\Plugin\Field\FieldFormatter\ImageFormatter</span>; </span></span><span> </span><span><span>/** </span></span><span><span> * Plugin implementation of the 'image_title_caption' formatter. </span></span><span><span> * </span></span><span><span> * @FieldFormatter( </span></span><span><span> * id = "image_title_caption", </span></span><span><span> * label = @Translation("Image with caption from title"), </span></span><span><span> * field_types = <span>{ </span></span></span><span><span> * "image" </span></span><span><span> * <span>} </span></span></span><span><span> * ) </span></span><span><span> */ </span></span><span><span>class ImageTitleCaption extends ImageFormatter { </span></span><span> </span><span> <span>/** </span></span><span><span> * <span>{@inheritdoc} </span></span></span><span><span> */ </span></span><span> <span>public function viewElements(FieldItemListInterface $items) { </span></span><span> <span>$elements = <span>parent::</span>viewElements($items); </span></span><span> <span>foreach ($elements as &$element) { </span></span><span> <span>$element['#theme'] = 'image_title_caption_formatter'; </span></span><span> <span>} </span></span><span> </span><span> <span>return $elements; </span></span><span> <span>} </span></span><span> </span><span><span>}</span></span>
Similar to Drupal 7, the name of this file is important as it mirrors the theme name. As for the contents, they are almost the same as the template used by the image_formatter theme except for the caption printed at the bottom.
Now that we’ve written the code, we need enable the module and clear all the caches if we had made code changes after enabling. It’s time to test it out.
For example, go to the article content type field display settings at admin/structure/types/manage/article/display. For the Image field, under the Format heading, you should be able to select the Image with caption from title format. Save the form and go to admin/structure/types/manage/article/fields/node.article.field_image and make sure the image field title is enabled.
Finally, you can edit an article, upload an image and specify a title. That title will continue to behave as such, but additionally, it will be displayed below the image as a caption. Of course, you can still style it as you wish etc.
In this article we’ve seen how easy it is to create a field formatter and extend default behaviour in Drupal 8. We’ve only touched on overriding the viewElements() of this plugin but we can do much more to further customise things. You are also not required to extend the ImageFormatter. There are plenty of existing plugins you can either extend from or use as an example.
Additionally, you can easily create new field types and widgets as well. It’s a similar process but you’ll need to take some schema information into account, use different annotation classes and write some more code. But the point is you are very flexible in doing so.
Creating a custom field formatter in Drupal 8 involves several steps. First, you need to create a custom module if you don’t have one already. Then, in your custom module, create a new file in the src/Plugin/Field/FieldFormatter directory. The file should be named according to the class it will contain. Inside this file, you will define your custom field formatter class, which should extend the FormatterBase class. You will need to implement several methods, including viewElements() which is responsible for generating the render array for the field values.
The @FieldFormatter annotation in Drupal 8 is used to define a field formatter. It includes properties like id, label, and field_types. The id is a unique identifier for the formatter, the label is the human-readable name, and field_types is an array of field type machine names that the formatter supports.
To apply your custom field formatter to a field in Drupal 8, you need to go to the “Manage display” tab of the content type, taxonomy term, or other entity type that has the field. Find the field in the list and select your custom formatter from the “Format” dropdown. Then click the “Update” button and save the changes.
The output of your custom field formatter in Drupal 8 is controlled by the viewElements() method in your formatter class. This method should return a render array for the field values. You can use Drupal’s theming system to further customize the output.
Yes, you can use a custom field formatter for multiple field types in Drupal 8. In the @FieldFormatter annotation of your formatter class, you can specify an array of field type machine names in the field_types property.
To create a settings form for your custom field formatter in Drupal 8, you need to implement the settingsForm() and settingsSummary() methods in your formatter class. The settingsForm() method should return a form array for the settings, and the settingsSummary() method should return an array of summary lines for the settings.
To use a custom field formatter to display images in Drupal 8, your formatter class should extend the ImageFormatterBase class instead of FormatterBase. You will need to implement the viewElements() method to generate a render array for the image field values.
Yes, you can create a custom field formatter for a custom field type in Drupal 8. In the @FieldFormatter annotation of your formatter class, you can specify the machine name of your custom field type in the field_types property.
To use a custom field formatter to display links in Drupal 8, your formatter class should extend the LinkFormatter class instead of FormatterBase. You will need to implement the viewElements() method to generate a render array for the link field values.
To create a custom field formatter for a multi-value field in Drupal 8, your formatter class should extend the FormatterBase class and implement the viewElements() method. This method should return a render array for the field values, taking into account that the field may have multiple values.
The above is the detailed content of Creating Custom Field Formatters in Drupal 8. For more information, please follow other related articles on the PHP Chinese website!