Drupal is a registered trademark of Dries Buytaert

form_decorator

95 sites Security covered
View on drupal.org

This module introduces an innovative way to customize Drupal forms through decoration. Form decoration allows developers to modify and enhance form behavior by applying a layer of additional functionality, without altering the original form class directly.

In Drupal forms are provided by form classes. In the form class you have to provide several methods:

getFormId()
buildForm()
validateForm()
submitForm()

It is also possible to inject services using dependency injection and to implement additional methods that are useful for this form.

That is very nice! But there is no easy way to extend a form class. If you want to alter a form you have to rely on hook_form_alter. Of course that works just fine but you loose all the nice features of the original form class (No object, no dependency injection, you have to change submit/validation handlers).

The form_decorator module introduces an easy way to change the form class itself. And because we want to be able to alter one form multiple times we use decoration.

In short: The key benefit over hook_form_alter hooks and event based solutions like https://www.drupal.org/project/hook_event_dispatcher is that in a decorator you can in principle work like you would in the form class itself.

We try to get this into core

See #3422081

Examples

Add custom validation to the user register form:

#[FormDecorator('form_user_register_form_alter')]
final class ValidateOnly extends FormDecoratorBase {

  public function validateForm(array &$form, FormStateInterface $form_state) {
    $this->inner->validateForm($form, $form_state);
    // Do your magic
    }
  }

}

Use dependency injection:

#[FormDecorator('form_user_login_form_alter')]
final class DependencyInjection extends FormDecoratorBase implements ContainerFactoryPluginInterface {

  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('entity_type.manager')
    );
  }

  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    protected EntityTypeManagerInterface $entityTypeManager,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
  }

  public function buildForm(array $form, FormStateInterface $form_state, ...$args) {
    // Do your magic
  }

Allow to edit the created date of nodes:

#[FormDecorator('form_node_form_alter')]
final class NodeCreatedDate extends ContentEntityFormDecoratorBase {

  public function buildForm(array $form, FormStateInterface $form_state, ...$args) {
    $form = $this->inner->buildForm($form, $form_state, ...$args);
    $node = $this->getEntity();
    assert($node instanceof NodeInterface);
    $created_time = time();
    if (!$this->getEntity()->isNew()) {
      $created_time = $node->getCreatedTime();
    }

    $form['created_datepicker'] = [
      '#title' => $this->t('Created date'),
      '#type' => 'datetime',
      '#default_value' => DrupalDateTime::createFromTimestamp($created_time),
      '#weight' => 100,
    ];
    return $form;
  }

  public function save(array $form, FormStateInterface $form_state) {
    $node = $this->getEntity();
    assert($node instanceof NodeInterface);
    $node->setCreatedTime($form_state->getValue('created_datepicker')->getTimestamp());
    return $this->inner->save($form, $form_state);
  }

Activity

Total releases
4
First release
Jan 2025
Latest release
1 month ago
Release cadence
121 days
Stability
50% stable

Release Timeline

Releases

Version Type Release date
1.0.1 Stable Jan 27, 2026
1.0.0 Stable Jul 16, 2025
1.0.0-rc1 Pre-release May 28, 2025
1.0.0-beta1 Pre-release Jan 30, 2025