Skip to content

Mandatory Rules

When to Use

Use this checklist before completing ANY ECA plugin. These are not optional - violating these rules will cause bugs or break your plugin.

Decision

Every rule below is MANDATORY. Check each one before considering your plugin complete.

Pattern

create() Method Rules:

ALWAYS call parent::create() first ✅ NEVER use new static() when base class has dependencies ✅ ONLY inject services that are NEW to your plugin ✅ NEVER list parent's services (entity_type.manager, eca.token_services, etc.)

// Correct pattern
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
  $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
  $instance->myNewService = $container->get('my.new.service');
  return $instance;
}

Configuration Method Rules:

defaultConfiguration()ALWAYS merge: return [...] + parent::defaultConfiguration()buildConfigurationForm()ALWAYS call parent::buildConfigurationForm() LAST ✅ submitConfigurationForm()ALWAYS call parent::submitConfigurationForm()access()ALWAYS call parent::access() after your checks

// defaultConfiguration - merge
public function defaultConfiguration(): array {
  return ['my_field' => ''] + parent::defaultConfiguration();
}

// buildConfigurationForm - call parent LAST
public function buildConfigurationForm(array $form, FormStateInterface $form_state): array {
  $form['my_field'] = [...];
  return parent::buildConfigurationForm($form, $form_state);
}

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

// access - call parent after checks
public function access($object, ?AccountInterface $account = NULL, $return_as_object = FALSE) {
  if (!$this->isValid()) {
    return $return_as_object ? AccessResult::forbidden('Invalid.') : FALSE;
  }
  return parent::access($object, $account, $return_as_object);
}

Token Integration Rules:

✅ Add #eca_token_replacement => TRUE to text fields that should process tokens ✅ Add #eca_token_reference => TRUE to fields that define token names ✅ Use tokenService->getOrReplace() in execute(), NEVER direct config access ✅ Call parent::buildConfigurationForm() LAST to include token browser

Condition Rules:

ALWAYS wrap evaluate() result with negationCheck()NEVER override StringComparisonBase::evaluate() (it's FINAL) ✅ Override getLeftValue() and getRightValue() instead for comparisons ✅ Return bool from evaluate(), never NULL

Attribute Rules:

BOTH #[Action] AND #[EcaAction] required for actions ✅ BOTH #[EcaEvent] and deriver required for events ✅ #[EcaCondition] required for conditions ✅ Include version_introduced in ECA attributes

Pre-Completion Checklist:

  1. ✅ No parent constructor parameters duplicated in child
  2. ✅ Child only injects its OWN new dependencies
  3. ✅ All overridden methods call their parent
  4. StringComparisonBase::evaluate() is NOT overridden
  5. ✅ All text fields have appropriate #eca_token_* attributes
  6. execute() uses tokenService->getOrReplace() for config values
  7. ✅ Conditions wrap result with negationCheck()
  8. ✅ Both required attributes present

Common Mistakes

Every item in Common Pitfalls violates one of these mandatory rules. Review that section for examples of what NOT to do.

See Also

References: - Core: /modules/contrib/eca/src/Plugin/ECA/EcaPluginBase.php (FINAL constructor)