Skip to main content
Category:

In this tuto, I'll show you how to add custom fields on configuration Block  entity using the ThirdPartySettingsInterface .

By default configuration Block  entity fields is like this:

 

by deafult

 

In this article we are going to look at how to use the ThirdPartySettingsInterface to add some extra fields to existing configuration block entity.

Today we are going to see an example of this and add an extra fields to the block definition and store the value in this way.

There are a number of steps involved in this process. First, we need to alter the form with which the entity configuration data is added and saved. 

In this example  I'll add custom css class, ID, custom attributes and wrapper tag fields to each block.

Create mymodule.module file:

<?php


use Drupal\block\BlockInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\block\Entity\Block;
use Drupal\Component\Utility\Html;


/**
 * Implements hook_form_FORM_ID_alter().
 */
function mymodule_form_block_form_alter(&$form, FormStateInterface $form_state, $form_id)
{
  $block = $form_state->getFormObject()->getEntity();

  // This will automatically be saved in the third party settings.
  // instead of used this in submit handler $block->setThirdPartySetting('module_name', 'field_name', $value);
  $form['third_party_settings']['#tree'] = TRUE;

  /**
   * use this syntax for save values in the third party settings
   * $form['third_party_settings']['module_name']['field_name']
   **/
  $form['third_party_settings']['mymodule']['classes'] = [
    '#type' => 'textfield',
    '#title' => t('CSS class(es)'),
    '#description' => t('Customize the styling of this block by adding CSS classes.'),
    '#default_value' => $block->getThirdPartySetting('mymodule', 'classes'),
  ];

  $form['third_party_settings']['mymodule']['id'] = [
    '#type' => 'textfield',
    '#title' => t('ID'),
    '#description' => t('Customize the ID of this block.'),
    '#default_value' => $block->getThirdPartySetting('mymodule', 'id'),
  ];

  $form['third_party_settings']['mymodule']['attributes'] = [
    '#type' => 'textfield',
    '#title' => t('Others Attributes'),
    '#description' => t('Others Attributes like this format "attr|value,attr1|value1" '),
    '#default_value' => $block->getThirdPartySetting('mymodule', 'attributes'),
  ];

  $form['third_party_settings']['mymodule']['wrapper_tag'] = [
    '#type' => 'textfield',
    '#title' => t('Wrapper Tag'),
    '#description' => t('Wrapper Tag "div or section or ....." default is div  '),
    '#default_value' => $block->getThirdPartySetting('mymodule', 'wrapper_tag'),
  ];

}


/**
 * override hook_ENTITY_TYPE_presave().
 */
function mymodule_block_presave(BlockInterface $entity) {
  //  delete classes from  Third Party Settings if classes is empty
  if (empty($entity->getThirdPartySetting('mymodule', 'classes'))) {
    $entity->unsetThirdPartySetting('mymodule', 'classes');
  }
  //  delete id from  Third Party Settings if id is empty
  if (empty($entity->getThirdPartySetting('mymodule', 'id'))) {
    $entity->unsetThirdPartySetting('mymodule', 'id');
  }

  //  delete attributes from  Third Party Settings if attributes is empty
  if (empty($entity->getThirdPartySetting('mymodule', 'attributes'))) {
    $entity->unsetThirdPartySetting('mymodule', 'attributes');
  }

  //  delete wrapper_tag from  Third Party Settings if wrapper_tag is empty
  if (empty($entity->getThirdPartySetting('mymodule', 'wrapper_tag'))) {
    $entity->unsetThirdPartySetting('mymodule', 'wrapper_tag');
  }

}

 

The getThirdPartySetting(), setThirdPartySetting and unsetThirdPartySetting  methods on the entity object is provided by the ThirdPartySettingsInterface which all configuration entities have by default if they extend from the ConfigEntityBase class. 

We need also to add our configuration schema so that it becomes translatable. Inside the /config/schema/mymodule.schema.yml file of our module we need to add this:

config/schema/mymodule.schema.yml

block.block.*.third_party.mymodule:
  type: mapping
  label: Block attributes third party settings
  mapping:
    classes:
      type: string
      label: add more classes for the block
    id:
      type: string
      label: update id for the block
    attributes:
      type: string
      label: add/updates attributes for the block
    wrapper_tag:
      type: string
      label: update wrapper tag for the block

 

With this schema definition we are basically appending to the schema of the block.block config block entity by specifying some metadata about the third party settings our module provides. For more information on config schemas be sure to check out the docs on Drupal.org.

After adding our custom fields  to existing config block entity the result is like this:

 

our custom fields

 

To add these fields to each block I will use hook_preprocess_block().


/**
 * Implements hook_preprocess_HOOK().
 */
function mymodule_preprocess_block(&$variables)
{
  $variables['wrapper_tag'] = "div";

  // add this condition Blocks coming from page manager widget does not have id.
  if (!empty($variables['elements']['#id'])) {
    $block = Block::load($variables['elements']['#id']);
    if ($block) {
      // add classes to attributes
      if ($classes = $block->getThirdPartySetting('mymodule', 'classes')) {
        $classes_array = explode(' ', $classes);
        foreach ($classes_array as $class) {
          $variables['attributes']['class'][] = Html::cleanCssIdentifier($class, []);
        }
      }

      // update id of the block
      if ($id = $block->getThirdPartySetting('mymodule', 'id')) {
        $variables['attributes']['id'] = $id;
      }

      // add custom attributes to block
      if ($attributes = $block->getThirdPartySetting('mymodule', 'attributes')) {
        $attributes_array = explode(',', $attributes);
        foreach ($attributes_array as $attribute) {
          $attr = explode('|', $attribute);
          if (isset($attr[0]) && isset($attr[1])) {
            $variables['attributes'][$attr[0]] = $attr[1];
          }
        }
      }

      // add wrapper tag variable to block
      if ($wrapper_tag = $block->getThirdPartySetting('mymodule', 'wrapper_tag')) {
        $variables['wrapper_tag'] = $wrapper_tag;
      }


    }
  }
}

 

Alter the theme registry information to use our custom theme file.


/**
 * Implements hook_theme_registry_alter().
 */
function mymodule_theme_registry_alter(&$theme_registry)
{
  $theme_registry['block']['path'] = drupal_get_path('module', 'mymodule') . '/templates';
}

 

Create mymodule/templates/block.html.twig file:

I use this file for override "wrraper_tag" by default is "div", but now is customizable.

 

{#
/**
 * @file
 * Default theme implementation to display a block.
 *
 * Available variables:
 * - plugin_id: The ID of the block implementation.
 * - label: The configured label of the block if visible.
 * - configuration: A list of the block's configuration values.
 *   - label: The configured label for the block.
 *   - label_display: The display settings for the label.
 *   - provider: The module or other provider that provided this block plugin.
 *   - Block plugin specific settings will also be stored here.
 * - content: The content of this block.
 * - attributes: array of HTML attributes populated by modules, intended to
 *   be added to the main container tag of this template.
 *   - id: A valid HTML ID and guaranteed unique.
 * - title_attributes: Same as attributes, except applied to the main title
 *   tag that appears in the template.
 * - content_attributes: Same as attributes, except applied to the main content
 *   tag that appears in the template.
 * - title_prefix: Additional output populated by modules, intended to be
 *   displayed in front of the main title tag that appears in the template.
 * - title_suffix: Additional output populated by modules, intended to be
 *   displayed after the main title tag that appears in the template.
 *
 * @see template_preprocess_block()
 *
 * @ingroup themeable
 */
#}

<{{ wrapper_tag }}{{ attributes }}>
  {{ title_prefix }}
  {% if label %}
    <h2{{ title_attributes }}>{{ label }}</h2>
  {% endif %}
  {{ title_suffix }}
  {% block content %}
    <div{{ content_attributes.addClass('content') }}>
      {{ content }}
    </div>
  {% endblock %}
</{{ wrapper_tag }}>

 

Finally, here's the result after adding CSS classes, ID, custom attributes and wrapper tag to config block entity.

 

result

 

 

Riadh Rahmi

Senior Web Developer PHP/Drupal & Laravel

I am a senior web developer, I have experience in planning and developing large scale dynamic web solutions especially in Drupal & Laravel.