Skip to content

Loops

ECA does not have a dedicated "loop" element in its modeling palette. However, iterating over a set of items is a common requirement, and ECA provides several approaches to achieve loop-like behavior.

Iterating over lists with Views

The most common pattern for looping in ECA uses a Views query combined with a custom event:

  1. Execute a View using the Views: execute a query action. The View returns a list of entities matching your criteria.
  2. For each entity in the result set, ECA automatically triggers a custom event, forwarding the current entity as a token.
  3. A separate ECA model (or the same model listening for the custom event) processes each entity individually.
1
2
3
Event → [Execute View] → for each result → [Custom Event per entity]
                                                    ↓
                                            [Process single entity]

This pattern effectively creates a loop: the View provides the list, and the custom event fires once per item, allowing you to perform actions on each entity.

Iterating with list operations

ECA provides actions for working with lists (arrays) of items:

  • List: loop through items -- iterates over a list token and triggers a custom event for each item. This is the most straightforward loop mechanism.
  • List: count items -- retrieves the number of items in a list, useful for conditional logic based on list size.
  • List: add/remove items -- modifies a list, which can be combined with other patterns for accumulating results.

Recursive custom events

For scenarios where the number of iterations is not known in advance, or where you need conditional looping (similar to a "while" loop in programming), you can use a recursive pattern:

  1. An action triggers a custom event with relevant tokens.
  2. The model listening for that custom event performs work and checks a condition.
  3. If the condition indicates more iterations are needed, the model triggers the same custom event again with updated tokens.

Avoid infinite loops

When using recursive custom events, always ensure there is a clear termination condition. An infinite loop will eventually exhaust PHP's execution time or memory limits and may cause your site to become unresponsive. Use a counter token or a clear exit condition, and consider adding a safety limit (e.g. stop after 100 iterations). ECA applies its own recursion guards as well, but treat those as a safety net rather than as a substitute for your own termination condition.

Built-in recursion guards

Independently of any limit you build into a model, ECA ships two guards against runaway recursion. Both are always active, and each one works on its own:

  • eca.max_recursion_level (default 1) -- detects a repeating block on the execution stack, meaning the same sequence of steps starting again while it is still running.
  • eca.max_event_occurrences (default 10) -- new in ECA 3.1.x. Caps how many times the same event may sit on the execution stack at once.

The second guard exists because the first one does not actually bound how far the stack can grow. Two models that trigger each other can nest indefinitely without ever repeating a complete block, so the repetition rule never fires and the process simply runs until PHP exhausts its memory. Counting occurrences of an event bounds the stack by construction, whatever shape the recursion takes.

Raising the event occurrence limit

A model that legitimately nests the same event more than ten levels deep -- recursive processing of a tree of taxonomy terms or a deep menu hierarchy, for example -- is halted at the tenth occurrence and logged as recursion, even though nothing is wrong with it.

To raise the cap, override the container parameter in a services file. This is not a setting in the user interface, and it takes effect only after a container rebuild:

1
2
parameters:
  eca.max_event_occurrences: 25

Raise it deliberately, though. Reaching the default is far more often a sign that the model really is missing a terminating condition than a sign that ten levels is too few.

Entity reference traversal

When you need to process entities related to a given entity (e.g. all paragraphs in a node, all nodes in a taxonomy term), you can:

  1. Use Entity: load referenced entities to retrieve the referenced entities.
  2. Iterate over the results using a list loop or Views-based pattern.

Practical example: send email to all users with a role

A common use case is notifying all users who have a specific role:

  1. Event: Content is created (or any other trigger).
  2. Action: Execute a View that returns all users with the target role.
  3. Custom event: Fires once per user found by the View.
  4. Action (in the custom event model): Send an email to the current user, using tokens from both the original content and the user entity.

Token forwarding

When using custom events for looping, remember to list all required tokens in the "Tokens to forward" field of the Trigger custom event action. Without this, the tokens from the original process will not be available inside the loop iteration.

Performance considerations

  • Views-based loops are efficient for entity-based iteration because they use database queries.
  • Recursive loops add overhead per iteration. Keep the loop body lightweight and avoid deeply nested recursion.
  • If you need to process a very large number of items, consider using cron-based processing or a queue system to avoid timeout issues.