Skip to content

Action Plugin Basics

When to Use

Create custom Action plugins when you need to perform operations that existing ECA actions cannot handle, such as integrating with external APIs, processing custom data structures, or implementing specialized business logic.

These guides document ECA 3.1.6, the current stable tag on the 3.1.x branch. Where they say "3.1" they mean a feature that arrived with the 3.1 line and holds across it, not a looser version claim.

Decision

If you need... Use Base Class Why
User-configurable action ConfigurableActionBase Provides form building, token support, configuration storage
Simple action with no config ActionBase Minimal overhead for straightforward operations
Form field manipulation FormFieldActionBase Built-in form field lookup and modification logic
Key-value storage operations KeyValueStoreBase Integrated KV store access patterns

Pattern

<?php
namespace Drupal\my_module\Plugin\Action;

use Drupal\Core\Action\Attribute\Action;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\eca\Attribute\EcaAction;
use Drupal\eca\Plugin\Action\ConfigurableActionBase;

#[Action(
  id: 'my_module_custom_action',
  label: new TranslatableMarkup('My Module: Custom Action'),
  type: 'entity'  // Optional: passes entity context
)]
#[EcaAction(
  description: new TranslatableMarkup('Performs custom business logic.'),
  version_introduced: '1.0.0',
)]
class CustomAction extends ConfigurableActionBase {

  public function defaultConfiguration(): array {
    return [
      'input_field' => '',
      'result_token' => '',
    ] + parent::defaultConfiguration();
  }

  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
    $form['input_field'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Input value'),
      '#default_value' => $this->configuration['input_field'],
      '#eca_token_replacement' => TRUE,
      '#eca_token_reference' => TRUE,
    ];

    $form['result_token'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Result token name'),
      '#default_value' => $this->configuration['result_token'],
      '#required' => TRUE,
    ];

    return parent::buildConfigurationForm($form, $form_state);
  }

  public function submitConfigurationForm(array &$form, FormStateInterface $form_state): void {
    $this->configuration['input_field'] = $form_state->getValue('input_field');
    $this->configuration['result_token'] = $form_state->getValue('result_token');
    parent::submitConfigurationForm($form, $form_state);
  }

  public function execute(): void {
    $input = $this->tokenService->getOrReplace($this->configuration['input_field']);

    // Business logic
    $result = $this->performOperation($input);

    // Store result for downstream actions
    $this->tokenService->addTokenData($this->configuration['result_token'], $result);
  }
}

Common Mistakes

  • Missing parent::buildConfigurationForm() call → Token browser won't appear (ALWAYS call parent LAST)
  • Missing #eca_token_replacement attribute → Tokens won't be replaced in field values
  • Accessing $this->configuration directly in execute() → Ignores token replacement (use tokenService->getOrReplace())
  • Using new static() in create() → Breaks when parent changes (use parent::create())
  • Missing both #[Action] and #[EcaAction] attributes → Plugin won't register properly

See Also

References: - Core: /modules/contrib/eca/src/Plugin/Action/ConfigurableActionBase.php - Example: /modules/contrib/eca/modules/base/src/Plugin/Action/EcaStateWrite.php