Skip to content

Conditional

The conditional step type evaluates a registered conditional handler against workflow variables and branches the workflow based on the result. It always produces a 'true' or 'false' outcome that determines the transition path.

Definition

php
'check_amount' => [
    'type' => 'conditional',
    'handler' => 'is_high_value',
]

Required Fields

FieldTypeDescription
typestringMust be 'conditional'
handlerstringRegistry name or fully-qualified class name of a Conditional implementation

Additional keys in the step definition are accessible to the handler via $context->getStepConfig().

Conditional Handlers

Each conditional is a class that implements the Conditional interface:

php
use Workflowable\Workflowable\Contracts\Conditional;
use Workflowable\Workflowable\Engine\ExecutionContext;

class IsHighValue implements Conditional
{
    public function evaluate(ExecutionContext $context): bool
    {
        $threshold = $context->getStepConfig('threshold', 1000);
        $amount = $context->getVariable('amount');

        return $amount >= $threshold;
    }
}

The handler receives the full ExecutionContext, giving it access to:

  • Workflow variables via getVariable() — data from the event and previous step outputs
  • Step config via getStepConfig() — parameters set in the workflow definition for this step

Registration

Conditionals are registered in the ConditionalRegistry, just like actions.

Global Registration (config)

Register conditionals available to all workflows in config/workflowable.php:

php
'conditionals' => [
    'is_high_value' => \App\Conditionals\IsHighValue::class,
    'is_eligible' => \App\Conditionals\IsEligible::class,
],

Event-Scoped Registration

Register conditionals only available to workflows for a specific event:

php
'events' => [
    'order_submitted' => [
        'class' => \App\WorkflowEvents\OrderSubmitted::class,
        'description' => 'Triggered when a new order is submitted',
        'actions' => [
            'process_order' => \App\Actions\ProcessOrder::class,
        ],
        'conditionals' => [
            'is_high_value' => \App\Conditionals\IsHighValue::class,
        ],
    ],
],

Programmatic Registration

php
$registry = app(ConditionalRegistry::class);
$registry->register('is_high_value', IsHighValue::class, 'Checks if order exceeds threshold');

Conditional Parameters

Conditionals can define their own parameters by implementing the DefinesParameters interface. This enables validation at workflow definition time:

php
use Workflowable\Workflowable\Contracts\Conditional;
use Workflowable\Workflowable\Contracts\DefinesParameters;
use Workflowable\Workflowable\Engine\ExecutionContext;
use Workflowable\Workflowable\Support\Parameters\NumberParameter;
use Workflowable\Workflowable\Support\Parameters\TextParameter;

class IsHighValue implements Conditional, DefinesParameters
{
    public static function parameters(): array
    {
        return [
            TextParameter::make('variable', 'Variable Name')
                ->rules(['string']),
            NumberParameter::make('threshold', 'Threshold')
                ->default(1000),
        ];
    }

    public function evaluate(ExecutionContext $context): bool
    {
        $variable = $context->getStepConfig('variable', 'amount');
        $threshold = $context->getStepConfig('threshold', 1000);

        return $context->getVariable($variable) >= $threshold;
    }
}

Then in the workflow definition, parameters are passed as step config:

php
'check_amount' => [
    'type' => 'conditional',
    'handler' => 'is_high_value',
    'variable' => 'order_total',
    'threshold' => 5000,
]

Required parameters without defaults are validated when the workflow definition is saved.

Handler Resolution

When the engine executes a conditional step:

  1. It looks up the handler name in the ConditionalRegistry
  2. If not found, it falls back to treating handler as a fully-qualified class name
  3. The class must implement the Conditional interface
  4. The handler's evaluate() method is called with the ExecutionContext
  5. Returns StepResult::success() with condition: 'true' or 'false' (used for transition routing)

Transitions

Conditional steps must define transitions for both 'true' and 'false' outcomes:

php
'transitions' => [
    'check_amount' => [
        'true' => 'high_value_approval',
        'false' => 'standard_process',
    ],
]

Both paths can lead to the same step if needed:

php
'transitions' => [
    'check_amount' => [
        'true' => 'process_order',
        'false' => 'process_order',
    ],
]

Either path can lead to a terminal state:

php
'transitions' => [
    'check_eligibility' => [
        'true' => 'process_application',
        'false' => 'failed',
    ],
]

Chaining Conditions

Chain multiple conditional steps to create complex decision trees:

php
'steps' => [
    'check_amount' => [
        'type' => 'conditional',
        'handler' => 'is_high_value',
        'threshold' => 1000,
    ],
    'check_priority' => [
        'type' => 'conditional',
        'handler' => 'is_priority',
    ],
    'standard_process' => [
        'type' => 'action',
        'handler' => 'standard_process',
    ],
    'priority_process' => [
        'type' => 'action',
        'handler' => 'priority_process',
    ],
    'high_value_review' => [
        'type' => 'action',
        'handler' => 'high_value_review',
    ],
],
'transitions' => [
    'check_amount' => [
        'true' => 'high_value_review',
        'false' => 'check_priority',
    ],
    'check_priority' => [
        'true' => 'priority_process',
        'false' => 'standard_process',
    ],
    'high_value_review' => 'completed',
    'standard_process' => 'completed',
    'priority_process' => 'completed',
]

Common Patterns

Gate Check

Use a conditional as a gate to short-circuit the workflow:

php
'steps' => [
    'check_eligible' => [
        'type' => 'conditional',
        'handler' => 'is_eligible',
    ],
    'process' => [
        'type' => 'action',
        'handler' => 'process',
    ],
],
'transitions' => [
    'check_eligible' => [
        'true' => 'process',
        'false' => 'failed',
    ],
    'process' => 'completed',
]

Configurable Threshold

Use step config to make a conditional reusable across different steps:

php
'steps' => [
    'check_order_value' => [
        'type' => 'conditional',
        'handler' => 'is_high_value',
        'variable' => 'order_total',
        'threshold' => 5000,
    ],
    'check_shipping_cost' => [
        'type' => 'conditional',
        'handler' => 'is_high_value',
        'variable' => 'shipping_cost',
        'threshold' => 100,
    ],
],

Boolean Flag Check

Check a flag set by a previous step:

php
'steps' => [
    'validate_order' => [
        'type' => 'action',
        'handler' => 'validate_order',
        // Returns: ['is_valid' => true] or ['is_valid' => false]
    ],
    'check_valid' => [
        'type' => 'conditional',
        'handler' => 'is_truthy',
        'variable' => 'is_valid',
    ],
],
'transitions' => [
    'validate_order' => 'check_valid',
    'check_valid' => [
        'true' => 'process_order',
        'false' => 'failed',
    ],
]