Custom Display Suite Fields in Drupal 8

Share this article

Without question, Display Suite is one of the most popular modules in Drupal’s contributed modules history. It allows the creation of layouts, fields and exposes all sorts of other powerful tools we use to build the presentation layer of our Drupal sites. Drupal 8 logo One of the more powerful features of Display Suite (DS) is the ability to create custom fields that can be displayed inside DS layouts alongside the actual core field values. In Drupal 7, this has been a very popular way of building layouts and showing dynamic data that is not strictly related to the output of any Field API field on the node (or other) entity. Display Suite has been ported and is being maintained for Drupal 8. Depending on another contributed module called Layout Plugin, the D8 version offers much of what we have available in Drupal 7 and probably even more. In this article, we are going to look at how we can create our own Display Suite field in Drupal 8 using the new OOP architecture and plugin system. To demonstrate this, we are going to create a DS field available only on the Article nodes that can be used to display a list of taxonomy terms from a certain vocabulary. And we’re going to make it so that the latter can be configured from the UI, namely admins will be able to specify which vocabulary’s terms should be listed. Not much usefulness in this example, I know, but it will allow you to understand how things work. If you are following along, the code we write is available in this repository inside the Demo module. So feel free to check that out.

Drupal 8 plugins

Much of the functionality that used to be declared using an _info hook in Drupal 7 is now declared using plugins in Drupal 8. For more information on using plugins and creating your own plugin types, make sure you check out a previous Sitepoint article that talks about just that. Display Suite also uses the new plugin system to allow other modules to define DS fields. It exposes a DsField plugin type which allows us to write and maintain all the necessary logic for such a field inside a single plugin class (+ any services we might inject into it). So we no longer implement hook_ds_field_info() and return an array of field information per entity type, but create a plugin class with data straight in its annotation and the relevant logic inside its methods.

VocabularyTerms class

Let us start by creating our plugin class called VocabularyTerms inside the src/plugins/DsField folder of our custom module and annotating it for our purposes:
namespace Drupal\demo\Plugin\DsField;

use Drupal\ds\Plugin\DsField\DsFieldBase;

/**
 * Plugin that renders the terms from a chosen taxonomy vocabulary.
 *
 * @DsField(
 *   id = "vocabulary_terms",
 *   title = @Translation("Vocabulary Terms"),
 *   entity_type = "node",
 *   provider = "demo",
 *   ui_limit = {"article|*"}
 * )
 */
class VocabularyTerms extends DsFieldBase {
}
This class alone will hold all of our logic for our very simple DsField plugin. But here are a couple of remarks about what we have so far:
  • The annotation is quite self explanatory: it provides meta information about the plugin.
  • The class extends DsFieldBase which provides base functionality for all the plugins of this type.
  • At the time of writing, the ui_limit annotation has just been committed to HEAD so it might not be available in the release you are using. Limiting the availability of the field on content types and view modes can be done by overriding the isAllowed() method of the base class and performing the logic there.

Default configuration

We want our field to be configurable: the ability to select from a list of existing vocabularies. So let’s start off by providing some defaults to this configuration so that if the user selects nothing, the Tags vocabulary which comes with core will be used. For this, we have to implement the defaultConfiguration() method:
/**
 * {@inheritdoc}
 */
public function defaultConfiguration() {

  $configuration = array(
    'vocabulary' => 'tags',
  );

  return $configuration;
}
And since we only have one configuration option, we return an array with one element keyed by the configuration name. That’s about it.

Formatters

We also want to have the ability to specify from the UI if the list of taxonomy terms is a series of links to their term pages or formatter as plain text. We could implement this within the configuration realm but let’s do so using formatters instead. And it’s very simple: we implement the formatters() method and return an array of available formatters:
/**
 * {@inheritdoc}
 */
public function formatters() {
  return array('linked' => 'Linked', 'unlinked' => 'Unlinked');
}
DS Field Formatter Options These will be available for selection in the UI under the Field heading of the Manage Display page of the content type. And we’ll be able to see the choice when we are building the actual field for display. But more on that in a second.

Configuration summary

It’s also recommended that if we are using UI defined settings, we have a summary of what has been selected as a simple string that describes it. This gets printed under the Widget heading of the Manage Display page of the content type. DS Configuration Summary To do this, we need to implement the settingsSummary() method and return said text:
/**
 * {@inheritdoc}
 */
public function settingsSummary($settings) {
  $config = $this->getConfiguration();
  $no_selection = array('No vocabulary selected.');

  if (isset($config['vocabulary']) && $config['vocabulary']) {
    $vocabulary = Vocabulary::load($config['vocabulary']);
    return $vocabulary ? array('Vocabulary: ' . $vocabulary->label()) : $no_selection;
  }

  return $no_selection;
}
Here we start getting more intimate with the actual configuration that was stored with the field, available by calling the getConfiguration() method on our plugin class. What we do above, then, is check if the vocabulary setting has been set, we load it based on its machine name using the Vocabulary class and return an array of strings that need to be printed. Since we are referencing the Vocabulary class, we also need to use it at the top:
use Drupal\taxonomy\Entity\Vocabulary;
Important to note: I am using Vocabulary statically here to load an entity for the sake of brevity. It is highly recommended you inject the relevant storage using dependency injection and use that to load entities. The same goes for most classes you’ll see me referencing statically below.

Settings form

Now that we display which configuration has been chosen from the UI, it’s time to provide the actual form which will allow the user to do so. This will be made available by clicking the cogwheel under the Operations heading of the Manage Display page of the content type. DS Field Settings Form
/**
 * {@inheritdoc}
 */
public function settingsForm($form, FormStateInterface $form_state) {
  $config = $this->getConfiguration();

  $names = taxonomy_vocabulary_get_names();
  $vocabularies = Vocabulary::loadMultiple($names); 
  $options = array();
  foreach ($vocabularies as $vocabulary) {
    $options[$vocabulary->id()] = $vocabulary->label();
  }
  $settings['vocabulary'] = array(
    '#type' => 'select',
    '#title' => t('Vocabulary'),
    '#default_value' => $config['vocabulary'],
    '#options' => $options,
  );

  return $settings;
}
Like before, we need to implement a method for this. And what we do inside is load all the taxonomy vocabulary names and prepare an array of options to be used with a Form API select list. The latter is the only element we need for this form.

Rendering the field

The last thing left to do is implement the build() method responsible for rendering the contents of our field:
/**
 * {@inheritdoc}
 */
public function build() {
  $config = $this->getConfiguration();
  if (!isset($config['vocabulary']) || !$config['vocabulary']) {
    return;
  }

  $query = \Drupal::entityQuery('taxonomy_term')
    ->condition('vid', $config['vocabulary']);

  $tids = $query->execute();
  if (!$tids) {
    return;
  }

  $terms = Term::loadMultiple($tids);
  if (!$terms) {
    return;
   }

  return array(
    '#theme' => 'item_list',
    '#items' => $this->buildTermList($terms),
  );
}
So what do we do here? First, we access the chosen vocabulary from the configuration. Then we run an EntityQuery
to find all the terms in this vocabulary. Next, we load all these terms and finally we return a render array that uses the item_list theme to print our terms. Although we don’t need it here, in most cases you’ll need to access the node entity that is currently being rendered. That is available inside the configuration array under the entity key. Moreover, under the build key you have the actual render array of the node being built. So keep this in mind and do inspect the other elements of the configuration array on your own for more information. I would like to mention a few more things before we take a look at the actual buildTermList() method. First, for brevity, we used the EntityQuery service statically. In your project, you should inject it. Second, we used the Term class statically to load the taxonomy term entities. Again, you should inject its storage and use that for this purpose. And lastly, we should import the Term class at the top with use:
use Drupal\taxonomy\Entity\Term;
Now that this is clear, let’s take a look at our own buildTermList() method:
private function buildTermList(array $terms) {
  $config = $this->getConfiguration();
  $formatter = isset($config['field']['formatter']) && $config['field']['formatter'] ? $config['field']['formatter'] : 'unlinked';
  $items = array();
  foreach ($terms as $term) {
    $items[] = $this->buildTermListItem($term, $formatter);
  }

  return $items;
}
This method is responsible for getting the field formatter, looping through the term entities and building an array of term information that can be printed using the item_list theme. As you can see, though, the individual term entity and formatter are passed to yet another helper method to keep things nice and tidy:
private function buildTermListItem(Term $term, $formatter) {
  if ($formatter === 'linked') {
    $link_url = Url::fromRoute('entity.taxonomy_term.canonical', array('taxonomy_term' => $term->id()));
    return \Drupal::l($term->label(), $link_url);
  }

  return SafeMarkup::checkPlain($term->label());
}
Finally, in the buildTermListItem() method we either return the sanitized title of the term or a link to it depending on the formatter. Again we see classes which should be injected but were used statically to save some space. With the risk of sounding like a broken record, keep in mind that you should inject these. For now, we must use them at the top:
use Drupal\Core\Url;
use Drupal\Component\Utility\SafeMarkup;

Conclusion

And there we have it, our very own DsField plugin in Drupal 8. Clearing the caches would now make this field available on all view modes of the Article content type. Moreover, it can be configured to choose among multiple vocabularies, the terms of which it will then display. And finally, we can even specify a formatter to print these terms either linked or as plain text.

Frequently Asked Questions on Custom Display Suite Fields in Drupal 8

What are the benefits of using Custom Display Suite Fields in Drupal 8?

Custom Display Suite Fields in Drupal 8 offer a range of benefits. They provide a flexible and easy way to manage the display of your content. With these fields, you can control how your content is displayed in different view modes, such as full content, teaser, or search results. This allows you to create a consistent look and feel across your site. Additionally, you can add custom fields to your display without having to write any code. This makes it a great tool for site builders who may not have extensive coding knowledge.

How do I create a Custom Display Suite Field in Drupal 8?

Creating a Custom Display Suite Field in Drupal 8 is a straightforward process. First, navigate to the “Manage Display” tab of your content type. Then, click on the “Custom Display Settings” dropdown and enable the view mode you want to customize. After that, click on the “Manage Display” link next to the view mode. Here, you can add, remove, and rearrange fields. To add a custom field, click on the “Add a field” button, select the type of field you want to add, and configure its settings.

Can I use Custom Display Suite Fields with any content type in Drupal 8?

Yes, you can use Custom Display Suite Fields with any content type in Drupal 8. This includes both default content types like Articles and Basic Pages, as well as any custom content types you create. This flexibility makes it a powerful tool for managing the display of all your site’s content.

What is the difference between Display Suite and the default Drupal 8 display management?

Display Suite offers more advanced features compared to the default Drupal 8 display management. With Display Suite, you can create custom fields, use different layouts for different view modes, and even control the display of individual fields within a view mode. On the other hand, the default Drupal 8 display management only allows you to control the order of fields in a view mode.

Can I use Display Suite with other Drupal 8 modules?

Yes, Display Suite is compatible with many other Drupal 8 modules. For example, you can use it with the Views module to create complex displays of your content. You can also use it with the Field Group module to group related fields together in your display.

How can I customize the layout of my content with Display Suite?

Display Suite allows you to choose from a variety of layouts for your content. You can select a layout when you create a custom field or when you manage the display of a view mode. Once you’ve chosen a layout, you can drag and drop fields into different regions of the layout.

Can I use Display Suite to create responsive layouts?

Yes, Display Suite supports responsive design. You can choose from a variety of responsive layouts, or you can create your own. This allows you to ensure that your content looks great on all devices, from desktops to smartphones.

How do I update Display Suite?

You can update Display Suite through the Drupal 8 administrative interface. Navigate to the “Extend” tab, find Display Suite in the list of modules, and click the “Update” link. Be sure to backup your site before updating any module.

Can I use Display Suite to display content in a grid?

Yes, Display Suite includes a variety of grid layouts. You can choose a grid layout when you create a custom field or when you manage the display of a view mode. Once you’ve chosen a grid layout, you can drag and drop fields into the grid.

What should I do if I encounter problems with Display Suite?

If you encounter problems with Display Suite, you can seek help from the Drupal community. The Drupal website has a variety of resources, including documentation, forums, and issue queues. You can also contact the maintainers of the Display Suite module directly.

Daniel SiposDaniel Sipos
View Author

Daniel Sipos is a Drupal developer who lives in Brussels, Belgium. He works professionally with Drupal but likes to use other PHP frameworks and technologies as well. He runs webomelette.com, a Drupal blog where he writes articles and tutorials about Drupal development, theming and site building.

BrunoSdrupaldrupal 8drupal-planetdrupal8OOPHPPHP
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week