Drupal is a registered trademark of Dries Buytaert
Protected Pages 3.0.0 Major update available for module protected_pages (3.0.0). Commerce Core 3.3.8 Minor update available for module commerce (3.3.8). Search API HTML Element Filter 1.0.7 Minor update available for module search_api_html_element_filter (1.0.7). Layout Builder Reorder 2.0.1 Minor update available for module layout_builder_reorder (2.0.1). Ban 1.1.0 Minor update available for module ban (1.1.0). Field Formatter Range 2.0.0 Major update available for module field_formatter_range (2.0.0). Field Formatter Range 8.x-1.8 Minor update available for module field_formatter_range (8.x-1.8). UI Patterns (SDC in Drupal UI) 2.0.18 Minor update available for module ui_patterns (2.0.18). Search API Solr 4.3.11 Module search_api_solr updated after 14 months of inactivity (4.3.11). Provus Mega Menu Module provus_mega_menu crossed 1,000 active installs.

Form Decorator

134 sites Security covered
View on drupal.org

This module provides a way to customize Drupal forms by decorating them. Form decoration allows developers to modify form behavior by adding functionality without altering the original form class, offering benefits similar to working within the form class itself, such as dependency injection.

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
6
First release
Jan 2025
Latest release
5 days ago
Releases (12 mo)
3
Maintenance
Active

Release Timeline

Releases

Version Type Release date
1.1.0 Stable Jul 12, 2026
1.1.x-dev Dev Jul 12, 2026
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