Skip to main content
Drupal is a registered trademark of Dries Buytaert
Release: OpenID Connect / OAuth client 3.0.0-alpha9 New alpha version released for module openid_connect (3.0.0-alpha9). Usage Milestone: Google Analytics Module google_analytics crossed 1,000 active installs. Release: Timelinr 1.0.1 Minor update available for module timelinr (1.0.1). Release: GraphQL Compose Codegen 1.1.2 Minor update available for module graphql_compose_codegen (1.1.2). Release: Mapy.com 1.1.3 Minor update available for module mapycom (1.1.3). Release: Ckeditor5 entity browser 3.0.3 Minor update available for module ckeditor5_entity_browser (3.0.3). Release: Ckeditor5 entity browser 3.0.1 Minor update available for module ckeditor5_entity_browser (3.0.1). Release: Ckeditor5 entity browser 3.0.2 Minor update available for module ckeditor5_entity_browser (3.0.2). Release: Teamleader Integration 4.0.2 Minor update available for module teamleader (4.0.2). Module Revived: Entityqueue Buttons 1.1.2 Module entityqueue_buttons updated after 8 months of inactivity (1.1.2).

Form Decorator

120 sites Security covered Drupal 10–11
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);
  }

Depends on

Dependencies of the latest stable release

No dependencies recorded for this project.

Required by

Tracked projects that depend on this one

No tracked projects depend on this one yet.

Activity

Tracked releases
7
Tracked since
Jan 2025
Latest release
1 month ago
Releases (12 mo)
3 ▼ from 4
Maintenance
Active

Release Timeline

Releases

Version Type Core Release date
1.1.0 Stable 10–11 Jul 12, 2026
1.1.x-dev Dev 10–11 Jul 12, 2026
1.0.1 Stable 10–11 Jan 27, 2026
1.0.0 Stable 10–11 Jul 16, 2025
1.0.0-rc1 Pre-release 10–11 May 28, 2025
1.0.0-beta1 Pre-release 10–11 Jan 30, 2025
1.0.0-alpha3 Pre-release 10–11 Jan 13, 2025