In one of my projects I wanted to save configuration (coming from a plugin) to content entities. In Drupal however, configuration is saved to configuration entities (or configuration objects) and managed using the very solid configuration management tools.
If Drupal provides solid solutions for configuration, maybe the problem I was trying to solve was an XY problem? Was I trying to find the answer to the wrong question? Even the documentation on configuration management suggested content had nothing to do with configuration:
Configuration is the collection of admin settings that determine how the site functions, as opposed to the content of the site.
However, I discovered that several contrib modules have come across the same need to save configuration to content entities and have solved this problem in different ways.
For example, the Views Reference module saves configuration for rendering a View to a serialized
string that is part of a custom Field Type. Other modules provide generic solutions that you could easily implement in your own module: the Plugin module and Commerce come up with an agnostic field type for saving plugin configuration of any type.
In this post I will try to explain how this works using an example module.
Before I dive in to the specifics, you might still not be convinced of the validity of my problem: So let’s look at the difference between Content and Config entities in Drupal:
|Config Entity||Content Entity|
|Limited by design||Unlimited by design|
|Created by developer/site builder||Created by editor/end user|
So if you need editors to create and save items of configuration in unlimited amounts, not the Config Entity but the Content Entity might be the way to go.
The Plugin Field Type
The Plugin and Commerce modules both implement an almost identical way of saving configuration to content entities: they both provide a new field type for saving plugin Configuration and use ‘derivatives’ for the specific Plugin types to use for each field 1.
To illustrate this, I’ve created a module called ‘Shapes’ implementing the Field type provided by the Commerce module (but it could just as easily have been Plugin module). In my module, a user with the right permissions will be allowed to create new ‘Shape’ content entities (as many as he or she likes). The eligible shapes are plugins of type ‘Shape’. The currently available shapes are: circle, ellipse, rectangle and star. Any new shape would just be a matter of adding the Plugin for that particular shape.
Each ‘shape’ plugin instance requires an array of configuration. In case of the circle it’s a ‘radius’, the ellipse requires the ‘X radius’ and the ‘Y radius’, the rectangle the ‘height’ and the ‘width’ and the Star requires an ‘inner radius’, ‘outer radius’ and the ‘amount of points’ for the star. As you can see the configuration for each of these shapes could be quite arbitrary.
In order to save the configuration we have to add a field of type
commerce_plugin_item to a content entity. Note that behind the colon you find the derivative of the field type for our ‘Shape’ plugin type.
$fields['shape'] = BaseFieldDefinition::create('commerce_plugin_item:shape') ->setLabel(t('Shape')) ->setDisplayConfigurable('view', TRUE) ->setDisplayConfigurable('form', TRUE);
By implementing the
ConfigurablePluginInterface and the
PluginFormInterface which are also required for any ‘regular’ configurable plugin (such as Image effects, Blocks etc), you are forced to implement the methods needed for the form element of the configuration.
The ‘PluginSelectWidget’ provided by Commerce will automatically render the correct plugin configuration form using ajax, depending on the plugin selected.
You can try it out downloading the repo from: https://github.com/nuez/shapes
Food for thought
To achieve this without writing too much custom code, you need either the Plugin or the Commerce module, both with their significant overhead. Specially the Commerce module you don’t want to install just to be able to save configuration to a field.
Would it be worth it to have a dedicated module just for this purpose?
A part from that, the field type provided by either module doesn’t require a schema definition for the configuration. Therefore there is no type casting of configuration parameters or schema checking in tests, like there is for regular configuration. A schema could also helps translating those properties of the configuration that are actually translatable, instead of allowing a completely different configuration in a translated field, as is currently the case.
1. The only difference between Commerce and Plugin is the way they decide which Plugin types are eligible for the ‘Plugin field’. Commerce uses an Event Subscriber to which new Plugin Types can Subscribe. The Plugin module adds a Plugin Type Manager that relies on YAML definitions of the different Plugin Types, something that’s currently not provided by Drupal Core. ↩comments powered by Disqus