Skip to main content

Signals

Signals let external code push data or decisions into a running workflow. Inside handle(), awaitSignal() suspends the workflow until the named signal arrives, then returns its payload:

public function handle(): void
{
$decision = $this->awaitSignal('approval'); // suspends until delivered

if (($decision['approved'] ?? false) === true) {
$this->action(Publish::class)->run();
}
}

A signal delivered before the workflow awaits it is consumed inline without suspending.

Timeouts

The fluent form adds a deadline that turns an unanswered wait into a catchable exception:

use DiscoveryUkraine\SagaLaraFlow\Exceptions\AwaitSignalTimeoutException;

try {
$decision = $this->signal('approval')
->timeoutAfter(now()->addDay())
->wait();
} catch (AwaitSignalTimeoutException $e) {
$this->action(AutoReject::class)->run();
}

awaitSignal($name, $timeout) accepts the timeout as an optional second argument as well.

Delivering a signal

From anywhere in your app, deliver via the flow handle:

use DiscoveryUkraine\SagaLaraFlow\Facades\SagaFlow;

SagaFlow::loadFlow($runId)->signal('approval', ['approved' => true]);

signal() throws CannotSignalTerminalFlowException if the run has already finished. Use the safe variant to no-op instead:

$delivered = SagaFlow::loadFlow($runId)->signalIfRunning('approval', ['approved' => true]);
// $delivered === false on a terminal run

You can also deliver from the CLI — see Artisan commands:

php artisan saga-flow:signal {run} approval --payload='{"approved":true}'