Skip to main content

Using the engine in Laravel

The container binding

The service provider registers Engine as a singleton, constructed from config('expression-engine'). Resolve it however you normally resolve services:

use Qanna\ExpressionEngine\Engine;

class EvaluateWorkflowStep
{
public function __construct(private Engine $engine) {}

public function handle(array $context): bool
{
return (bool) $this->engine->evaluate($this->step->condition, $context);
}
}

Because it's a singleton, any custom methods or functions you register persist for the rest of the request — register them once, in a service provider's boot().

The facade

Qanna\ExpressionEngine\Facades\ExpressionEngine proxies the same singleton:

use Qanna\ExpressionEngine\Facades\ExpressionEngine;

ExpressionEngine::resolve('Hi {{ trigger.user.name }}', $context);
ExpressionEngine::evaluate('trigger.order.total > 100', $context);

Every method listed in the Engine API reference is available on the facade.

Templated payloads with resolveArray()

A common Laravel use is a stored action — a notification, an outgoing webhook, a queued job's parameters — whose fields contain {{ }} placeholders. resolveArray() resolves them all in one call:

$payload = ExpressionEngine::resolveArray([
'url' => $action->endpoint,
'headers' => [
'X-Order-Ref' => '{{ trigger.order.ref }}',
],
'body' => [
'customer' => '{{ trigger.order.customer.name }}',
'total' => '{{ trigger.order.total }}',
'line_count' => '{{ trigger.order.lines.count() }}',
'is_priority' => '{{ trigger.order.total > 500 }}',
],
], ['trigger' => ['order' => $order->toArray()]]);

Http::post($payload['url'], $payload['body']);

String fields that are a single placeholder keep their real type — total stays numeric, is_priority stays boolean, line_count stays an integer. See the single-expression exception.

Passing Eloquent data as context

The engine reads arrays, so hand it ->toArray() output (or any array you assemble):

ExpressionEngine::evaluate('trigger.order.lines.filter(current.shipped).count()', [
'trigger' => ['order' => $order->load('lines')->toArray()],
]);

Strict mode per call

The container binding uses the configured strict value. If one caller needs the opposite behaviour, construct a dedicated instance:

$strict = new Engine([...config('expression-engine'), 'strict' => true]);

Trying expressions interactively

php artisan expression:playground opens a REPL against a live engine and context — see the CLI reference. It's the fastest way to check what an expression does before you store it.