Custom triggers
A trigger is a node that starts a workflow. It extends
Qanna\WorkflowEngine\Trigger (itself a Node), so it has the same type() /
label() / schema() descriptors, plus:
handle(WorkflowContext $context, array $config = []): TriggerResult— decides whether an incoming payload should start an execution.register(WorkflowVersion $version): void— wires up whatever delivers events for a given workflow (a route, a cron entry, a model listener). Called at application boot once for the published version of each workflow using this trigger.
Generate one
php artisan workflow:make-node StripeEventTrigger --trigger --type=app::stripe-event
namespace App\Workflows\Triggers;
use Qanna\WorkflowEngine\Engine\Context\WorkflowContext;
use Qanna\WorkflowEngine\Engine\Schema\Schema;
use Qanna\WorkflowEngine\Models\WorkflowVersion;
use Qanna\WorkflowEngine\Trigger;
use Qanna\WorkflowEngine\TriggerResult;
class StripeEventTrigger extends Trigger
{
public static function type(): string
{
return 'app::stripe-event';
}
public static function label(): string
{
return 'Stripe event';
}
public static function schema(): array
{
return [
Schema::select('event')->options([
'invoice.paid' => 'Invoice paid',
'customer.subscription.deleted' => 'Subscription cancelled',
])->required(),
];
}
public function handle(WorkflowContext $context, array $config = []): TriggerResult
{
$payload = $context->get('payload');
if (($payload['type'] ?? null) !== $config['event']) {
return TriggerResult::ignore('event type mismatch');
}
return TriggerResult::continue($payload);
}
protected function register(WorkflowVersion $version): void
{
// e.g. nothing to do — your existing Stripe webhook controller calls
// Workflow::run($version->workflow_id, $stripeEvent) for every matching workflow.
}
}
TriggerResult
| Constructor | Meaning |
|---|---|
TriggerResult::continue($output = null) | Start an execution. $output is recorded as the trigger step's output. |
TriggerResult::ignore($reason = null) | Don't start. Workflow::run() returns null; a workflow.trigger-ignored hook fires with the reason. |
The engine resolves {{ }} expressions in the trigger's saved config before
calling handle(), the same as it does for nodes.
register()
Runs at boot for the published version of every workflow whose trigger type
matches. Workflows that have never been published are skipped. $version
carries the published content — $version->workflow_id, $version->version,
$version->trigger['config'], $version->nodes, and so on. Use it to subscribe
to the outside world. The built-in triggers show the patterns:
- Webhook registers a
POSTroute per workflow. - Schedule adds an entry to Laravel's scheduler.
- Model event attaches Eloquent event listeners.
Because this happens at boot, a newly published version's trigger is not wired up until the next boot — your app and queue workers must be restarted. The interactive builder reminds you of this.
Upgrading:
register()previously received theWorkflowmodel. Change the parameter toWorkflowVersion $versionand read the workflow id from$version->workflow_id.
Registering the trigger
use Qanna\WorkflowEngine\Engine\NodeRegistry;
$this->app->make(NodeRegistry::class)->register([
\App\Workflows\Triggers\StripeEventTrigger::class,
], 'trigger');
Registering under the 'trigger' category is what makes the engine call the
trigger's register() at boot and offer it in workflow:build.