# Developing Custom ECA Plugins

ECA provides Drush generators to scaffold plugin boilerplate. Enable the
`eca_development` sub-module (not for production):

```shell
drush en eca_development

drush gen eca-event       # event class + ECA event plugin + deriver + constants
drush gen eca-condition   # condition plugin
drush gen eca-action      # action plugin
```

## Custom Action Plugin

Actions extend `ConfigurableActionBase` from ECA:

```php
<?php

namespace Drupal\my_module\Plugin\Action;

use Drupal\Core\Form\FormStateInterface;
use Drupal\eca\Plugin\Action\ConfigurableActionBase;

/**
 * My custom ECA action.
 *
 * @Action(
 *   id = "my_module_custom_action",
 *   label = @Translation("My custom action"),
 *   description = @Translation("Does something custom."),
 *   eca_version_introduced = "3.0.0",
 *   type = "node"
 * )
 */
class MyCustomAction extends ConfigurableActionBase {

  public function execute(mixed $entity = NULL): void {
    // Your logic here. $entity is the context entity (e.g. node).
    // Use $this->tokenService to resolve tokens:
    $value = $this->tokenService->replaceClear(
      $this->configuration['my_setting']
    );
  }

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

  public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
    $form['my_setting'] = [
      '#type' => 'textfield',
      '#title' => $this->t('My setting'),
      '#default_value' => $this->configuration['my_setting'],
      '#description' => $this->t('This field supports tokens.'),
      '#eca_token_reference' => TRUE,
    ];
    return parent::buildConfigurationForm($form, $form_state);
  }

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

}
```

Key points:

- `#eca_token_reference => TRUE` marks a field as supporting tokens in
  the modeler UI.
- Use `$this->tokenService->replaceClear()` to resolve token references.
- The `type` annotation key restricts the action to specific entity
  types. Omit it for entity-type-agnostic actions.
- Default values defined in `defaultConfiguration()` pre-fill the UI,
  but users can clear them. Always validate in `execute()`.

## Custom Condition Plugin

Conditions extend `ConditionBase` from ECA:

```php
<?php

namespace Drupal\my_module\Plugin\ECA\Condition;

use Drupal\eca\Plugin\ECA\Condition\ConditionBase;

/**
 * My custom condition.
 *
 * @EcaCondition(
 *   id = "my_module_custom_condition",
 *   label = @Translation("My custom condition"),
 *   description = @Translation("Checks something custom."),
 *   eca_version_introduced = "3.0.0"
 * )
 */
class MyCustomCondition extends ConditionBase {

  public function evaluate(): bool {
    // Return TRUE if the condition is met, FALSE otherwise.
    // Negation is handled automatically by the base class.
    $value = $this->tokenService->replaceClear(
      $this->configuration['field_to_check']
    );
    return !empty($value);
  }

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

  // Add buildConfigurationForm / submitConfigurationForm as needed.

}
```

Key points:

- Return only the positive logic in `evaluate()`. The `negate` option
  is handled automatically by `ConditionBase`.
- Use the `@EcaCondition` annotation (not `@Condition`).

## Custom Event Plugin

Events require more scaffolding: a Symfony event class, an ECA event
plugin, and often a deriver for filtering by entity type/bundle.

The `drush gen eca-event` generator creates all required files. The
generated structure typically includes:

- A Symfony event class extending
  `Drupal\Component\EventDispatcher\Event`
- An ECA event plugin in `Plugin/ECA/Event/` annotated with
  `@EcaEvent`
- A deriver class if the event should be filtered by entity type or
  bundle
- A constants class or interface for event name strings

For entity-aware events, extend the appropriate ECA base class (e.g.
`ContentEntityBaseEvent`) to get automatic entity token handling.

## Developer Guidelines

- Default values in plugins: define in `defaultConfiguration()` to
  pre-fill the UI. But always validate in `access()` and `execute()`
  since users can clear fields.
- Configuration values need to be checked in all use cases. Set to
  default if expectation is not met.
- Use `$this->tokenService` for all token operations.
- Mark token-supporting fields with `#eca_token_reference => TRUE`.
