Install the Bundle
composer require devhelp/flow-control-bundle:dev-master
Register the bundle in config/bundles.php:
return [
// ...
Devhelp\FlowControlBundle\DevhelpFlowControlBundle::class => ['all' => true],
];
Define a Basic Flow
Add a flow configuration in config/packages/devhelp_flow_control.yaml:
devhelp_flow_control:
flows:
simple_flow:
moves:
step1: [step1]
step2: [step1, step2]
step3: [step1, step2, step3]
Annotate a Controller
Use the @FlowStep annotation on controller actions:
use Devhelp\FlowControlBundle\Annotation\FlowStep;
class CheckoutController extends AbstractController
{
/**
* @FlowStep("simple_flow", "step1")
*/
public function configureOrder(): Response
{
// Logic for step1
}
/**
* @FlowStep("simple_flow", "step2")
*/
public function reviewOrder(): Response
{
// Logic for step2
}
}
Test the Flow
Access the annotated routes. The bundle will enforce the flow order, returning a 403 Forbidden if accessed out of sequence.
Multi-Step Forms Use flows to enforce sequential steps in multi-page forms (e.g., checkout, onboarding):
checkout_flow:
moves:
address: [address]
review: [address, review]
payment: [address, review, payment]
Conditional Flows Dynamically adjust flows based on user roles or data:
// In a controller or service
$flowName = $user->isPremium() ? 'premium_flow' : 'standard_flow';
Reusable Flow Logic
Extract flow definitions to a dedicated config file (e.g., config/flows/checkout.yaml) and load them via config/packages/devhelp_flow_control.yaml:
devhelp_flow_control:
flows: *import('flows/checkout.yaml')
Integration with Symfony Events
Listen to flow_control.pre_validate or flow_control.post_validate events to customize validation logic:
// src/EventListener/FlowListener.php
public function onPreValidate(FlowEvent $event)
{
if ($event->getFlowName() === 'checkout_flow' && !$event->getUser()->hasCart()) {
$event->setAllowed(false);
}
}
Flow-Aware Templates Pass the current flow step to Twig for UI hints:
{% if flow_step == 'payment' %}
<div class="highlight">Final step!</div>
{% endif %}
_route parameters to pass the flow name/step:
# config/routes.yaml
checkout_address:
path: /checkout/address
controller: App\Controller\CheckoutController::address
defaults: { _route_flow: 'checkout_flow', _route_step: 'address' }
$constraints = new Collection([
new FlowStep('checkout_flow', 'payment'),
new Assert\NotBlank(),
]);
$this->validator->validate($data, $constraints);
Caching Issues Clear the cache after modifying flow configurations:
php bin/console cache:clear
The bundle does not auto-reload configs during development.
Annotation Overrides If using both annotations and route parameters, the annotation takes precedence. Avoid mixing them unless intentional.
Flow Name Sensitivity
Flow names in configs and annotations must match exactly (case-sensitive). Typos will silently fail with 403.
Circular Dependencies
The bundle does not detect circular flow definitions (e.g., step1: [step2], step2: [step1]). Validate manually or use a tool like Graphviz to visualize flows.
Symfony 5+ Compatibility The bundle lacks explicit Symfony 5/6 support. Test with:
composer require symfony/*:^5.0 --dev
Enable Debug Mode
Set devhelp_flow_control.debug: true in config to log flow validation attempts:
devhelp_flow_control:
debug: true
Check logs for entries like:
[FlowControl] Validating step 'payment' for flow 'checkout_flow'...
Inspect Current Step Access the current step in controllers via:
$currentStep = $this->get('devhelp_flow_control.flow_manager')->getCurrentStep('checkout_flow');
Override Validation
Implement a custom validator by extending Devhelp\FlowControlBundle\Validator\FlowValidator and bind it as a service.
Custom Storage
Replace the default flow storage (e.g., for session-based flows) by implementing Devhelp\FlowControlBundle\Storage\FlowStorageInterface.
Event Dispatching Extend the bundle’s event system to trigger actions on flow transitions:
// config/services.yaml
Devhelp\FlowControlBundle\EventListener\FlowTransitionListener:
tags:
- { name: kernel.event_listener, event: flow_control.post_validate, method: onTransition }
Dynamic Flows
Load flows from a database or API by implementing Devhelp\FlowControlBundle\Loader\FlowLoaderInterface.
UI Integration Add a Twig extension to display flow progress:
// src/Twig/FlowExtension.php
public function getFlowProgress(string $flowName): array
{
return $this->flowManager->getProgress($flowName);
}
Usage in Twig:
{% for step in flow_progress %}
{{ step }} {% if not loop.last %}/{% endif %}
{% endfor %}
How can I help you explore Laravel packages today?