solution-forest/workflow-engine-laravel
The WorkflowBuilder class provides a fluent API for creating workflows.
create(string $name): staticCreates a new workflow builder instance. Name must start with a letter and contain only letters, numbers, hyphens, and underscores.
$builder = WorkflowBuilder::create('my-workflow');
description(string $description): selfSets a human-readable description for the workflow.
$builder->description('Handles complete user onboarding process');
version(string $version): selfSets the workflow version for change tracking.
$builder->version('2.1.0');
addStep(string $id, string|WorkflowAction $action, array $config = [], string|int|null $timeout = null, int $retryAttempts = 0): selfAdds a custom action step to the workflow. Timeout accepts seconds as integer or string format ('30s', '5m', '2h', '1d'). Retry attempts must be between 0 and 10.
$builder->addStep('process-payment', ProcessPaymentAction::class);
$builder->addStep('process-payment', ProcessPaymentAction::class, ['currency' => 'USD'], 30, 3);
$builder->addStep('slow-task', SlowAction::class, timeout: '5m', retryAttempts: 3);
email(string $template, string $to, string $subject, array $data = []): selfAdds an email step to the workflow.
$builder->email('welcome-email', '{{ user.email }}', 'Welcome {{ user.name }}!', ['welcome_bonus' => 100]);
http(string $url, string $method = 'GET', array $data = [], array $headers = []): selfAdds an HTTP request step to the workflow.
$builder->http('https://api.example.com/webhooks', 'POST', [
'event' => 'user_registered',
'user_id' => '{{ user.id }}'
]);
delay(int $seconds = null, int $minutes = null, int $hours = null): selfAdds a delay step to the workflow.
$builder->delay(minutes: 30);
$builder->delay(hours: 2);
$builder->delay(hours: 1, minutes: 30); // 1.5 hour delay
condition(string $condition): selfAdds a condition check step for workflow branching.
$builder->condition('user.verified === true');
when(string $condition, callable $callback): selfAdds conditional logic to the workflow. Steps added in the callback are only executed when the condition is met.
$builder->when('user.age >= 18', function($builder) {
$builder->addStep('verify-identity', VerifyIdentityAction::class);
});
startWith(string|WorkflowAction $action, array $config = [], string|int|null $timeout = null, int $retryAttempts = 0): selfAdds the first step in a workflow (syntactic sugar for better readability). Auto-generates a step ID.
$builder->startWith(ValidateInputAction::class, ['strict' => true]);
then(string|WorkflowAction $action, array $config = [], string|int|null $timeout = null, int $retryAttempts = 0): selfAdds a sequential step (syntactic sugar for better readability). Auto-generates a step ID.
$builder->then(ProcessDataAction::class)->then(SaveResultAction::class);
withMetadata(array $metadata): selfAdds custom metadata to the workflow definition.
$builder->withMetadata([
'author' => 'John Doe',
'department' => 'Engineering',
'priority' => 'high'
]);
build(): WorkflowDefinitionBuilds and returns the workflow definition.
$workflow = $builder->build();
quick(): QuickWorkflowBuilder (static)Returns a QuickWorkflowBuilder instance for pre-built common workflow patterns.
$workflow = WorkflowBuilder::quick()->userOnboarding('new-user-flow');
$workflow = WorkflowBuilder::quick()->orderProcessing();
$workflow = WorkflowBuilder::quick()->documentApproval();
The WorkflowAction interface defines the contract for all workflow step implementations.
execute(WorkflowContext $context): ActionResultExecute the workflow action with the provided context. This is the core method where the action's business logic is implemented.
canExecute(WorkflowContext $context): boolCheck if this action can be executed with the given context. Allows for pre-execution validation and conditional logic.
getName(): stringGet the human-readable display name for this action.
getDescription(): stringGet a detailed description of what this action does.
class CreateUserProfileAction implements WorkflowAction
{
public function execute(WorkflowContext $context): ActionResult
{
$userData = $context->getData('user');
$profile = UserProfile::create(['user_id' => $userData['id']]);
return ActionResult::success(['profile_id' => $profile->id]);
}
public function canExecute(WorkflowContext $context): bool
{
return $context->hasData('user.id');
}
public function getName(): string
{
return 'Create User Profile';
}
public function getDescription(): string
{
return 'Creates a new user profile in the database';
}
}
The WorkflowContext class holds the data that flows through the workflow. It is an immutable final readonly class.
readonly string $workflowIdThe unique workflow instance ID.
readonly string $stepIdThe ID of the current step.
readonly array $dataThe workflow data.
readonly array $configStep-specific configuration parameters.
readonly ?WorkflowInstance $instanceThe associated workflow instance (if available).
readonly DateTime $executedAtTimestamp when this context was created.
getData(?string $key = null, mixed $default = null): mixedGets data from the context. Without parameters returns all data. Supports dot notation for nested access.
$allData = $context->getData();
$user = $context->getData('user');
$userName = $context->getData('user.name');
$email = $context->getData('user.email', 'unknown@example.com');
with(string $key, mixed $value): staticCreates a new context with a single data value set. Supports dot notation.
$newContext = $context->with('user.verified', true);
$newContext = $context->with('order.items.0.quantity', 2);
withData(array $newData): staticCreates a new context with additional data merged in (immutable operation).
$newContext = $context->withData([
'order' => ['id' => 123, 'total' => 99.99],
'payment' => ['method' => 'credit_card']
]);
hasData(string $key): boolCheck if a data key exists in the context. Supports dot notation.
if ($context->hasData('user.email')) {
// User email is available
}
getConfig(?string $key = null, mixed $default = null): mixedGet configuration value(s) for the current step. Supports dot notation.
$timeout = $context->getConfig('timeout', 30);
$retries = $context->getConfig('retry.attempts', 3);
$allConfig = $context->getConfig(); // Gets all configuration
getWorkflowId(): stringGet the workflow identifier.
getStepId(): stringGet the current step identifier.
toArray(): arrayConvert the context to an array representation for serialization.
Enum representing workflow states.
PENDING - Workflow is created but not startedRUNNING - Workflow is currently executingWAITING - Workflow is waiting for external input or conditionsPAUSED - Workflow is temporarily pausedCOMPLETED - Workflow finished successfullyFAILED - Workflow failed with an errorCANCELLED - Workflow was cancelledisActive(): boolReturns true for active states (PENDING, RUNNING, WAITING, PAUSED).
if ($state->isActive()) {
// Workflow can still progress
}
isFinished(): boolReturns true for terminal states (COMPLETED, FAILED, CANCELLED).
if ($state->isFinished()) {
// Workflow execution has ended
}
isSuccessful(): boolReturns true only for COMPLETED state.
isError(): boolReturns true only for FAILED state.
color(): stringReturns a color name representing the state.
WorkflowState::PENDING->color(); // 'gray'
WorkflowState::RUNNING->color(); // 'blue'
WorkflowState::WAITING->color(); // 'yellow'
WorkflowState::PAUSED->color(); // 'orange'
WorkflowState::COMPLETED->color(); // 'green'
WorkflowState::FAILED->color(); // 'red'
WorkflowState::CANCELLED->color(); // 'purple'
icon(): stringReturns an emoji icon for the state.
WorkflowState::RUNNING->icon(); // '▶️'
WorkflowState::COMPLETED->icon(); // '✅'
WorkflowState::FAILED->icon(); // '❌'
label(): stringReturns a human-readable label.
WorkflowState::PENDING->label(); // 'Pending'
WorkflowState::RUNNING->label(); // 'Running'
WorkflowState::COMPLETED->label(); // 'Completed'
description(): stringReturns a detailed description of what the state means.
WorkflowState::RUNNING->description();
// 'The workflow is actively executing steps. One or more actions are currently being processed.'
canTransitionTo(WorkflowState $state): boolChecks if the state can transition to another state.
if ($currentState->canTransitionTo(WorkflowState::COMPLETED)) {
// Can transition to completed
}
Valid transitions:
PENDING -> RUNNING, CANCELLEDRUNNING -> WAITING, PAUSED, COMPLETED, FAILED, CANCELLEDWAITING -> RUNNING, FAILED, CANCELLEDPAUSED -> RUNNING, CANCELLEDCOMPLETED, FAILED, CANCELLED) -> nonegetValidTransitions(): arrayReturns all possible states that can be transitioned to from this state.
$validStates = WorkflowState::RUNNING->getValidTransitions();
// [WAITING, PAUSED, COMPLETED, FAILED, CANCELLED]
Represents the result of an action execution. This is an immutable value object.
success(array $data = [], array $metadata = []): staticCreates a successful result with optional data and metadata.
return ActionResult::success(['user_id' => 123]);
return ActionResult::success(
['processed_count' => 50],
['execution_time_ms' => 1250]
);
failure(string $errorMessage, array $metadata = []): staticCreates a failed result with an error message and optional metadata.
return ActionResult::failure('Payment failed');
return ActionResult::failure('API rate limit exceeded', [
'retry_after' => 3600,
'requests_remaining' => 0
]);
isSuccess(): boolWhether the action succeeded.
isFailure(): boolWhether the action failed.
getErrorMessage(): ?stringReturns the error message for failed results, or null for successful results.
getData(): arrayReturns the result data array. Empty array for failed results.
hasData(): boolReturns true if the result contains any data.
getMetadata(): arrayReturns additional execution metadata.
get(string $key, mixed $default = null): mixedGet a specific data value using dot notation.
$userId = $result->get('user.id');
$email = $result->get('user.email', 'N/A');
withMetadata(array $metadata): staticCreates a new result with additional metadata merged.
$resultWithMetadata = $result->withMetadata([
'execution_time' => 150,
'cache_hit' => true
]);
withMetadataEntry(string $key, mixed $value): selfCreates a new result with a single additional metadata entry.
mergeData(array $additionalData): selfCreates a new successful result by merging data. Throws LogicException if called on a failed result.
$result1 = ActionResult::success(['user_id' => 123]);
$result2 = $result1->mergeData(['email' => 'user@example.com']);
// result2 data: ['user_id' => 123, 'email' => 'user@example.com']
toArray(): arrayConvert the result to an array representation for serialization.
$array = $result->toArray();
// ['success' => true, 'error_message' => null, 'data' => [...], 'metadata' => [...]]
Helper class for simplified workflow creation and execution.
$simple = new SimpleWorkflow($storageAdapter, $eventDispatcher);
sequential(array $steps): stringCreates and executes a workflow with sequential steps. Returns the workflow instance ID.
$instanceId = $simple->sequential([
'step1' => StepOneAction::class,
'step2' => StepTwoAction::class,
'step3' => StepThreeAction::class
]);
runAction(string $actionClass, array $config = [], array $context = []): ActionResultExecutes a single action directly without creating a workflow.
executeBuilder(WorkflowBuilder $builder, array $context = []): stringExecutes a workflow from a builder instance.
resume(string $instanceId): WorkflowInstanceResumes a paused or pending workflow.
getStatus(string $instanceId): arrayGets the status of a workflow instance.
getEngine(): WorkflowEngineReturns the underlying workflow engine instance.
Pre-built workflow patterns for common business scenarios. Access via WorkflowBuilder::quick().
userOnboarding(string $name = 'user-onboarding'): WorkflowBuilderCreates a user onboarding workflow with standard steps (welcome email, delay, profile creation, role assignment).
orderProcessing(string $name = 'order-processing'): WorkflowBuilderCreates an order processing workflow (validate, charge payment, update inventory, confirmation email).
documentApproval(string $name = 'document-approval'): WorkflowBuilderCreates a document approval workflow (submit, assign reviewer, review request email, review, conditional approve/reject).
$workflow = WorkflowBuilder::quick()
->userOnboarding('premium-onboarding')
->then(SetupPremiumFeaturesAction::class)
->build();
Marks a class as a workflow step with metadata.
use SolutionForest\WorkflowEngine\Attributes\WorkflowStep;
#[WorkflowStep(
id: 'create_profile',
name: 'Create User Profile',
description: 'Creates a new user profile in the database',
config: ['template' => 'basic'],
required: true,
order: 1
)]
class CreateUserProfileAction implements WorkflowAction
{
// ...
}
Sets a timeout for an action. Accepts seconds, minutes, and/or hours. Provides a calculated totalSeconds property.
use SolutionForest\WorkflowEngine\Attributes\Timeout;
#[Timeout(seconds: 30)]
#[Timeout(minutes: 5)]
#[Timeout(minutes: 5, seconds: 30)] // 5 minutes 30 seconds
class MyAction implements WorkflowAction
{
// ...
}
Configures retry behavior for an action.
use SolutionForest\WorkflowEngine\Attributes\Retry;
#[Retry(attempts: 3, backoff: 'exponential')]
#[Retry(attempts: 5, backoff: 'exponential', delay: 1000, maxDelay: 30000)]
class MyAction implements WorkflowAction
{
// ...
}
Parameters:
attempts (int, default 3) - Number of retry attemptsbackoff ('linear' | 'exponential' | 'fixed') - Backoff strategydelay (int, default 1000) - Base delay in millisecondsmaxDelay (int, default 30000) - Maximum delay cap in millisecondsSets a condition for when an action should execute. This attribute is repeatable.
use SolutionForest\WorkflowEngine\Attributes\Condition;
#[Condition('user.email is not null')]
#[Condition('order.amount > 100')]
#[Condition('user.premium = true', operator: 'or')]
class ConditionalAction implements WorkflowAction
{
// ...
}
Parameters:
expression (string) - The condition expressionoperator ('and' | 'or', default 'and') - How to combine with other conditionsThe workflow engine dispatches the following events:
Dispatched when a workflow begins execution.
Properties: workflowId, name, context (array)
Dispatched when a workflow finishes successfully.
Properties: instance (WorkflowInstance)
Dispatched when a workflow fails due to errors.
Properties: instance (WorkflowInstance), exception (Throwable)
Dispatched when a workflow is cancelled.
Properties: workflowId, name, reason (string)
Dispatched when a step completes successfully.
Properties: instance, step, result
Dispatched when a step fails.
Properties: instance, step, exception (Throwable)
Base exception for all workflow exceptions. Provides getContext(), getUserMessage(), getDebugInfo(), getSuggestions().
Thrown for validation errors in workflow definitions. Factory methods: missingRequiredField(), invalidStep(), invalidStepId(), invalidRetryAttempts(), invalidTimeout(), duplicateStepId(), invalidName(), invalidCondition(), invalidDelay(), emptyWorkflow(), actionNotFound(), invalidActionClass().
Thrown when a workflow instance cannot be found. Factory methods: notFound(), malformedId(), storageConnectionError().
Thrown for invalid state transitions. Factory methods: cannotResumeCompleted(), cannotCancelFailed(), alreadyRunning(), fromInstanceTransition().
Thrown when an action class cannot be found or doesn't implement the interface. Factory methods: classNotFound(), invalidInterface(), actionNotFound(), invalidActionClass().
Thrown when a step fails during execution. Factory methods: actionClassNotFound(), invalidActionClass(), timeout(), fromException(), actionFailed().
How can I help you explore Laravel packages today?