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:
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:
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.