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 callparent::buildConfigurationForm()LASTsubmitConfigurationForm()→ ALWAYS callparent::submitConfigurationForm()access()→ ALWAYS callparent::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 => TRUEto text fields that should process tokens - Add
#eca_token_reference => TRUEto fields that define token names - Use
tokenService->getOrReplace()inexecute(), 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()andgetRightValue()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_introducedin 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)