List operations
filter(), map(), sort(), any(), every() (and its alias all())
run an expression once per item in a list. Inside that expression, the
item currently being processed is available as current.
$engine->evaluate('trigger.items.filter(current.active)', [
'trigger' => ['items' => [
['name' => 'Widget', 'active' => true],
['name' => 'Gadget', 'active' => false],
]],
]);
// → [['name' => 'Widget', 'active' => true]]
current
current is the per-item value. For a list of records it's the record;
for a list of scalars it's the scalar:
// list of records
$engine->evaluate('trigger.items.map(current.name)', $context);
// → ["Widget", "Gadget"]
// list of scalars
$engine->evaluate('trigger.scores.filter(current > 50)', ['trigger' => ['scores' => [40, 75, 90]]]);
// → [75, 90]
current can appear anywhere in the per-item expression — in a
comparison, inside a method chain, inside a nested method's arguments, or
inside a function call:
$engine->evaluate('trigger.items.filter(current.name.startsWith(current.prefix))', $context);
$engine->evaluate('trigger.items.filter(coalesce(current.active, false))', $context);
The operations
filter(predicate)
Keeps items for which the predicate is truthy. Returns a re-indexed list.
$engine->evaluate('trigger.orders.filter(current.total > 100)', $context);
map(transform)
Replaces each item with the result of the transform expression.
$engine->evaluate('trigger.items.map(current.price * 1.1)', $context);
$engine->evaluate('trigger.users.map(current.name.upper())', $context);
sort(by)
Sorts the list. With no argument, sorts values directly. With a current
expression, sorts by the value that expression produces for each item.
Does not mutate the input.
$engine->evaluate('trigger.nums.sort()', ['trigger' => ['nums' => [3, 1, 2]]]);
// → [1, 2, 3]
$engine->evaluate('trigger.items.sort(current.price)', $context);
any(predicate) / every(predicate) / all(predicate)
any() returns true if the predicate is truthy for at least one item.
every() (and its alias all()) returns true only if it's truthy for
every item. Both return a bool, not a list.
$engine->evaluate('trigger.cart.items.any(current.price > 100)', $context);
$engine->evaluate('trigger.cart.items.every(current.inStock)', $context);
Chaining and nesting
List operations return lists, so they chain with each other and with any other array method:
$engine->evaluate(
'trigger.items.filter(current.active).map(current.name.upper()).first()',
$context,
);
// → "WIDGET"
They also nest — a predicate can itself run a list operation over a nested list:
$engine->evaluate(
'trigger.orders.filter(current.items.any(current.price > 100)).map(current.id)',
$context,
);
// → orders that contain at least one item over 100
In a nested operation, current refers to the item of the innermost
operation currently running.
Non-current predicates pass through
filter() and map() only apply their per-item logic when the argument
actually references current. An argument that doesn't (a constant, or a
plain path) is passed to the underlying method as a value — in practice
you always want a current expression here.
Grouping
groupBy(key) is related but not a current operation — it buckets a
list of records by the value of a field:
$engine->evaluate('trigger.orders.groupBy("status")', $context);
// → ['paid' => [...], 'pending' => [...]]