Problem/Motivation

There is no reusable mechanism in Drupal for overriding config entity settings on a per-content-entity basis. For example, a Commerce store may need to override payment gateway credentials without modifying the global configuration, or a node may need to override a date format or notification settings specific to that piece of content.

This pattern is needed by multiple projects but each one implements it ad-hoc. A generic, plugin-based solution built on top of plugin_configuration_field would allow any module to declare overridable config entity fields with minimal boilerplate.

Proposed resolution

Add a new submodule config_entity_override (shipped under modules/) that provides:

1. Plugin type and manager

  • A ConfigEntityOverride PHP attribute declaring the plugin's target config entity type and, optionally, which content entity types it applies to via the entity_types parameter.
  • A ConfigEntityOverrideManager plugin manager with standard Drupal discovery.
  • Automatic registration as a plugin_configuration_field type via an event subscriber.

2. Base class and interface

  • ConfigEntityOverrideInterface exposes getEntityTypes() and appliesTo() so widgets and other consumers can check whether a plugin is valid for a given entity type.
  • ConfigEntityOverrideBase implements appliesTo() and provides template methods (buildOverrideForm, validateOverrideForm, submitOverrideForm) that concrete plugins override to expose only the config entity fields that should be editable.

3. Custom widget

  • ConfigEntityOverrideWidget extends the base plugin select widget.
  • Filters available plugins through getApplicableDefinitions(), keeping only those whose entity_types include the host entity type.
  • Renders a config entity select element alongside the plugin override form.

4. Data model

The field stores target_plugin_id (the override plugin ID) and target_plugin_configuration containing:

[
  'config_entity_id' => 'my_entity_id',
  'overrides' => ['setting' => 'overridden_value'],
]

No new database tables are needed; the submodule reuses the existing plugin_configuration_field schema.

5. Test coverage

  • Unit test: ConfigEntityOverrideBaseTest (appliesTo logic, entity type filtering).
  • Kernel tests: ConfigEntityOverrideManagerTest (plugin discovery), DateFormatOverrideTest (end-to-end config override).
  • FunctionalJavascript test: ConfigEntityOverrideWidgetTest (widget interaction, AJAX, form submission).
  • Test module config_entity_override_test with a DateFormatOverride plugin as a realistic example.

Usage example

use Drupal\config_entity_override\Attribute\ConfigEntityOverride;
use Drupal\config_entity_override\Plugin\ConfigEntityOverrideBase;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;

#[ConfigEntityOverride(
  id: 'my_config_type',
  label: new TranslatableMarkup('My config type'),
  config_entity_type: 'my_config_entity',
  entity_types: ['node'],
)]
class MyConfigOverride extends ConfigEntityOverrideBase {

  protected function buildOverrideForm(
    array $form,
    FormStateInterface $form_state,
    ConfigEntityInterface $config_entity,
  ): array {
    $form['setting'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Override value'),
      '#default_value' => $this->configuration['overrides']['setting']
        ?? $config_entity->get('setting'),
    ];
    return $form;
  }

}

Remaining tasks

  • Review and merge

User interface changes

  • New ConfigEntityOverrideWidget adds a config entity select dropdown above the override form when editing a plugin_configuration_field:config_entity_override field.

API changes

  • New plugin type: config_entity_override with attribute-based discovery.
  • New plugin manager service: plugin.manager.config_entity_override.
  • New interface: ConfigEntityOverrideInterface.
  • New base class: ConfigEntityOverrideBase.
  • New attribute: ConfigEntityOverride.
  • New widget: ConfigEntityOverrideWidget.

Data model changes

None. Reuses the existing plugin_configuration_field two-column schema (target_plugin_id + target_plugin_configuration).

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

  • 1.x Comparechanges, plain diff MR !3

Comments

facine created an issue. See original summary.

facine’s picture

Status: Active » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.