Drupal 8: custom data on configuration entities using the ThirdPartySettingsInterface
In this article we are going to look at how to use the ThirdPartySettingsInterface to add some extra data to existing configuration entities. For example, if you ever need to store some config together with a node type or a taxonomy vocabulary, there is a great way to do so using this interface. Today we are going to see an example of this and add an extra field to the menu 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 the case of the menu entity there are two forms (one for adding and one for editing) so we need to alter them both. We can do something like this:
/**
* Implements hook_form_alter().
*/
function my_module_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
if ($form_id === 'menu_add_form' || $form_id === 'menu_edit_form') {
my_module_alter_menu_forms($form, $form_state, $form_id);
}
}
Inside this general hook_form_alter()
implementation we delegate the logic to a custom function if the form is one of the two we need. Alternatively you can also implement hook_form_FORM_ID_alter()
for both those forms and delegate from each. That would limit a bit on the function calls. But let's see our custom function:
/**
* Handles the form alter for the menu_add_form and menu_edit_form forms.
*/
function my_module_alter_menu_forms(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
$menu = $form_state->getFormObject()->getEntity();
$form['my_text_field'] = array(
'#type' => 'textfield',
'#title' => t('My text field'),
'#description' => t('This is some extra data'),
'#default_value' => $menu->getThirdPartySetting('my_module', 'my_text_field'),
'#weight' => 1
);
if (isset($form['links'])) {
$form['links']['#weight'] = 2;
}
$form['#entity_builders'][] = 'my_module_form_menu_add_form_builder';
}
In here we do a couple of things. First, we retrieve the configuration entity object which the form is currently editing. Then, we define a new textfield and add it to the form. Next, we check if the form has menu links on it (meaning that it's probably the edit form) in which case we make its weight higher than one of our new field (just so that the form looks nicer). And last, we add a new #entity_builder
to the form which will be triggered when the form is submitted.
The getThirdPartySetting()
method on the entity object is provided by the ThirdPartySettingsInterface
which all configuration entities have by default if they extend from the ConfigEntityBase
class. With this method we simply retrieve a value that is stored as third party for a given module (my_module
in this case). It will return NULL if none is set so we don't even need to provide a default in this case.
Let us now turn to our #entity_builder
which gets called when the form is submitted and is responsible for mapping data to the entity:
/**
* Entity builder for the menu configuration entity.
*/
function my_module_form_menu_add_form_builder($entity_type, \Drupal\system\Entity\Menu $menu, &$form, \Drupal\Core\Form\FormStateInterface $form_state) {
if ($form_state->getValue('my_text_field')) {
$menu->setThirdPartySetting('my_module', 'my_text_field', $form_state->getValue('my_text_field'));
return;
}
$menu->unsetThirdPartySetting('my_module', 'my_text_field');
}
Inside we check if our textfield was filled in and set it to the third party setting we can access from the config entity object that is passed as an argument. If the form value is empty we reset the third party setting to remove lingering data in case there is something there.
And that's pretty much it for the business logic. We can clear the cache and try this out by creating/editing a menu and storing new data with it. However, our job is not quite finished. We need to add our configuration schema so that it becomes translatable. Inside the /config/schema/my_module.schema.yml
file of our module we need to add this:
system.menu.*.third_party.my_module: type: mapping label: 'My module textfield' mapping: my_text_field: type: text label: 'My textfield'
With this schema definition we are basically appending to the schema of the system.menu
config 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.
Now if we reinstall our module and turn on configuration translation, we can translate the values users add to my_text_field
. You go to admin/config/regional/config-translation/menu
, select a menu and when translating in a different language you see a new Third Party Settings
fieldset containing all the translatable values defined in the schema.
Hope this helps.