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:
- ✅ No parent constructor parameters duplicated in child
- ✅ Child only injects its OWN new dependencies
- ✅ All overridden methods call their parent
- ✅
StringComparisonBase::evaluate()is NOT overridden - ✅ All text fields have appropriate
#eca_token_*attributes - ✅
execute()usestokenService->getOrReplace()for config values - ✅ Conditions wrap result with
negationCheck() - ✅ 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
- Common Pitfalls for mistake examples
- Service Injection for create() details
- String Comparison Conditions for StringComparisonBase rules
References:
- Core: /modules/contrib/eca/src/Plugin/ECA/EcaPluginBase.php (FINAL constructor)