Adding functionality to contributed Drupal modules with ThirdPartySettings API

Posted July 16th, 2019

In an earlier post I demonstrated how to add some extra functionality on a node type in Drupal 8 via the ThirdPartySettings API. Its use is not just limited to core configuration types - it works for most (if not all) configuration entities. That means that any contributed module that provides a configuration entity can have additional configuration provided by you to solve certain needs.

On a recent project, we are employing the use of the Block Group contributed module to solve a certain requirement and add some authoring enhancements to the block interface. In short, it allows you to create 'groups' of which you can place multiple blocks in, and then place that group into a region. This allows for some flexibility and easier usage of the block admin interface, and reduces the need to have many multiple regions for a specific need.

Adding Functionality

A group configuration entity is pretty basic. It cannot have fields added to it or any other information other than the name of the block group. What if you wanted to add CSS classes or other flags on a group, then? We can turn to the ThirdPartySettings API yet again in a very similar pattern as before:

<?php

declare(strict_types = 1);

use Drupal\Core\Form\FormStateInterface;

function mymodule_blockgroup_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  if ($form_id == 'block_group_content_add_form' || $form_id == 'block_group_content_edit_form') {
    $entity = $form_state->getFormObject()->getEntity();

    $form['additional'] = array(
      '#type' => 'details',
      '#title' => t('Additional Settings'),
      '#group' => 'additional_settings',
    );

    $form['additional']['overlap'] = array(
      '#type' => 'checkbox',
      '#title' => t('Overlap'),
      '#description' => t('Check this box to add an overlap effect, overlapping the next element below.'),
      '#default_value' => $entity->getThirdPartySetting('mymodule_blockgroup', 'overlap'),
    );

    $form['additional']['uplap'] = array(
      '#type' => 'checkbox',
      '#title' => t('Uplap'),
      '#description' => t('Check this box to add an uplap effect, the opposite of overlap.'),
      '#default_value' => $entity->getThirdPartySetting('mymodule_blockgroup', 'uplap'),
    );

    $form['actions']['submit']['#submit'][] = 'mymodule_blockgroup_form_submit';
  }
}

/**
 * Form submit handler function for mymodule_blockgroup_form_alter().
 */
function mymodule_blockgroup_form_submit(&$form, FormStateInterface $form_state) {
  $entity = $form_state->getFormObject()->getEntity();
  $entity->setThirdPartySetting('mymodule_blockgroup', 'overlap', (bool) $form['additional']['overlap']['#value']);
  $entity->setThirdPartySetting('mymodule_blockgroup', 'uplap', (bool) $form['additional']['uplap']['#value']);
  $entity->save();
}

This time, we want to add two checkboxes to the admin form. If the author checks "Overlap" or "Uplap", this signals to the front end to add a modifier CSS class.

New admin screen with settings

Reading the values and passing to the template

Once we have some values, we can start reading it in from a preprocess hook to pass along to the block group template:

/**
 * Implements hook_preprocess_block().
 */
function mytheme_preprocess_block(&$variables) {
  if ($variables["elements"]["#base_plugin_id"] == 'block_group') {
    $variables["attributes"]['class'][] = 'promo-grid';

    $block = \Drupal::entityTypeManager()
      ->getStorage('block_group_content')
      ->load($variables['derivative_plugin_id']);

    $overlap = $block->getThirdPartySetting('mymodule_blockgroup', 'overlap');
    $uplap = $block->getThirdPartySetting('mymodule_blockgroup', 'uplap');

    if ($overlap) {
      $variables["attributes"]['class'][] = 'promo-grid--overlap';
    }

    if ($uplap) {
      $variables["attributes"]['class'][] = 'promo-grid--uplap';
    }
  }
}

Finally, provide a custom twig file for a block group entity itself (block--blockgroup.html.twig):

<section{{ attributes }}>
  {{ title_prefix }}
  {{ title_suffix }}
  {% block content %}
    {{ content }}
  {% endblock %}
</section>

The attributes passed in the preprocess hook are output automatically on the parent section tab by rendering the attributes template variable. We now have a rather flexible way to add classes to our block groups at the discretion of the site editor. Here is a block group with "Overlap" enabled, which the applied CSS class causes it to 'overlap' the next element, in this case, the site footer:

Site footer with overlap block group

Since the block group block entities are created with a deriver class, every block group configuration entity created in the admin can have its own unique settings applied and saved due to the ThirdPartySettings API. This simple addition now yields a lot more flexible functionality. 

Tags