Skip to main content

Building workflows

Two ways to author a workflow: in code, or interactively in the terminal. Both produce the same stored definition.

In code

Build the data structure and persist it through the repository:

use Qanna\WorkflowEngine\Engine\Nodes\Action\LogNode;
use Qanna\WorkflowEngine\Engine\Triggers\ManualTrigger;
use Qanna\WorkflowEngine\Models\Workflow;
use Qanna\WorkflowEngine\Storage\Contracts\WorkflowRepositoryContract;

$workflow = Workflow::fromArray([
'id' => 'send-welcome-email',
'name' => 'Send Welcome Email',
'trigger' => ['type' => ManualTrigger::type(), 'config' => []],
'nodes' => [
['id' => 'greet', 'type' => LogNode::type(), 'config' => [
'level' => 'info', 'message' => 'Welcome {{ trigger.email }}',
]],
],
'edges' => [
['from' => 'trigger', 'to' => 'greet', 'branch' => 'main'],
],
]);

app(WorkflowRepositoryContract::class)->create($workflow);

Edges

  • The entry edge is ['from' => 'trigger', 'to' => '<first node id>', 'branch' => 'main'].
  • Every other edge connects two node ids.
  • branch names which of the source node's outgoing branches the edge belongs to. For plain nodes that's main; for branching nodes see Control flow.
  • To iterate a Loop, add an edge from the last body node back to the loop node on main.

A branching example

'nodes' => [
['id' => 'check', 'type' => ConditionNode::type(), 'config' => [
'field' => '{{ trigger.plan }}', 'operator' => '==', 'value' => 'pro',
]],
['id' => 'provision-pro', 'type' => /* ... */],
['id' => 'provision-free', 'type' => /* ... */],
],
'edges' => [
['from' => 'trigger', 'to' => 'check', 'branch' => 'main'],
['from' => 'check', 'to' => 'provision-pro', 'branch' => 'true'],
['from' => 'check', 'to' => 'provision-free', 'branch' => 'false'],
],

Interactively: php artisan workflow:build

php artisan workflow:build # new, or pick an existing one to edit
php artisan workflow:build send-welcome-email # jump straight to that id

The command walks you through:

  1. Naming the workflow (the id is derived from the name; editing an existing one bumps its version on save).
  2. Choosing and configuring a trigger. Each trigger's schema is prompted field by field.
  3. A main menu loop to shape the graph:
    • Add a node — pick an attachment point and branch, pick a node type by category, give it an id, and fill in its config (plus connection / advanced settings if the node declares them). Adding after a node that already has a next step offers to insert between them.
    • Attach to an open branch — fill in a branch (e.g. a Condition's false) that has nothing wired to it yet.
    • Reconfigure / Remove / Undo last add / Reconnect a disconnected node.
    • Reconfigure the trigger.
  4. Finish and save — validates the graph (a trigger is required; the graph must be well-formed) and lets you save anyway if you choose. It reminds you to restart the app when the trigger needs boot-time wiring (webhook / schedule / model event).

Only nodes and triggers that opt into CLI support (cliSupported()) appear in the builder. Almost all built-ins do; the Switch node is the exception.

The builder renders a live tree of the workflow after every change, e.g.:

Workflow: Send Welcome Email (send-welcome-email) — 2 node(s)
Manual (trigger)
└─ greet (Log)
└─ check [true] (Condition)

Custom nodes

To make your own node types available to workflows (and the builder), see Custom nodes and workflow:make-node.