donutloop/restful-api-workflow-bundle
Installation Add the bundle via Composer:
composer require donutloop/restful-api-workflow-bundle
Enable the bundle in config/bundles.php:
DonutLoop\RestfulApiWorkflowBundle\DonutLoopRestfulApiWorkflowBundle::class => ['all' => true],
First Use Case: Basic Workflow Definition
Define a workflow in a YAML file (e.g., config/api_workflows/my_workflow.yml):
my_workflow:
steps:
- name: validate_input
action: validate
rules:
- field: name
required: true
- name: process_data
action: process
handler: App\Services\MyWorkflowService
Register the workflow in config/packages/donutloop_restful_api_workflow.yaml:
workflows:
my_workflow: "@=service('kernel')->getContainer()->loadFromExtension('api_workflows', 'my_workflow')"
Triggering a Workflow Inject the workflow service and execute:
use DonutLoop\RestfulApiWorkflowBundle\Workflow\WorkflowExecutor;
public function __construct(private WorkflowExecutor $workflowExecutor) {}
public function handle(Request $request)
{
$data = json_decode($request->getContent(), true);
$result = $this->workflowExecutor->execute('my_workflow', $data);
return json_encode($result);
}
Step Chaining
Use next_step to define conditional workflow progression:
steps:
- name: check_permission
action: validate
next_step: process_data
rules:
- field: user.role
value: admin
- name: process_data
action: process
Dynamic Handlers
Pass runtime data to handlers via handler_args:
steps:
- name: send_notification
action: call_handler
handler: App\Services\NotificationService
handler_args:
template: "@=data.email_template"
Error Handling
Define on_error steps to handle failures gracefully:
steps:
- name: risky_operation
action: call_handler
handler: App\Services\RiskyOperationService
on_error: fallback_step
kernel.request).WorkflowExecutor in PHPUnit:
$this->workflowExecutor->expects($this->once())->method('execute')->with('my_workflow', $data);
Circular Dependencies
Avoid recursive next_step references—workflows must terminate.
Handler Injection Ensure handlers are autowired or manually registered in the container:
services:
App\Services\MyWorkflowService: ~
Data Mutability
Workflows modify $data in-place. Clone input if immutability is required:
$result = $this->workflowExecutor->execute('my_workflow', clone $data);
DONUTLOOP_API_WORKFLOW_DEBUG: true in .env for step-by-step logs.executeWithDebug() to inspect workflow state:
$debugInfo = $this->workflowExecutor->executeWithDebug('my_workflow', $data);
Custom Actions
Implement DonutLoop\RestfulApiWorkflowBundle\Workflow\ActionInterface:
class CustomAction implements ActionInterface {
public function execute(array &$data, array $config) { ... }
}
Register in config/packages/donutloop_restful_api_workflow.yaml:
actions:
custom: App\Workflow\CustomAction
Dynamic Workflows
Load workflows from a database by extending WorkflowLoader:
class DatabaseWorkflowLoader implements WorkflowLoaderInterface {
public function load(string $name): array { ... }
}
Bind in services.yaml:
DonutLoop\RestfulApiWorkflowBundle\Workflow\WorkflowLoaderInterface: '@App\Workflow\DatabaseWorkflowLoader'
How can I help you explore Laravel packages today?