### Install CSS assets
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Include the required CSS in your base template and install assets via the console.
```twig
```
```sh
# in a shell
php bin/console assets:install --symlink web
```
--------------------------------
### Install CraueFormFlowBundle with Composer
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Use Composer to add the bundle to your project. This command downloads and installs the necessary files.
```sh
composer require craue/formflow-bundle
```
--------------------------------
### Create a Flow Class (Approach A)
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Define a flow class that extends `FormFlow` and configures the steps for your multi-step form. This example uses a single form type for multiple steps.
```php
// src/MyCompany/MyBundle/Form/CreateVehicleFlow.php
use Craue\FormFlowBundle\Form\FormFlow;
use Craue\FormFlowBundle\Form\FormFlowInterface;
use MyCompany\MyBundle\Form\CreateVehicleForm;
class CreateVehicleFlow extends FormFlow {
protected function loadStepsConfig() {
return [
[
'label' => 'wheels',
'form_type' => CreateVehicleForm::class,
],
[
'label' => 'engine',
'form_type' => CreateVehicleForm::class,
'skip' => function($estimatedCurrentStepNumber, FormFlowInterface $flow) {
return $estimatedCurrentStepNumber > 1 && !$flow->getFormData()->canHaveEngine();
},
],
[
'label' => 'confirmation',
],
];
}
}
```
--------------------------------
### Remove dedicated flow reset action
Source: https://github.com/craue/craueformflowbundle/blob/master/UPGRADE-3.0.md
Flows now start with clean data automatically, removing the need for a dedicated reset action.
```php
/**
* @Route("/create-topic/start/", name="createTopic_start")
*/
public function createTopicStartAction() {
$flow = $this->get('form.flow.createTopic');
$flow->reset();
return $this->redirect($this->generateUrl('createTopic'));
}
```
--------------------------------
### Configure step-based form options
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Pass individual options to specific steps using the form_options configuration or by overriding getFormOptions.
```php
// in src/MyCompany/MyBundle/Form/CreateVehicleFlow.php
protected function loadStepsConfig() {
return [
[
'label' => 'wheels',
'form_type' => CreateVehicleStep1Form:class,
'form_options' => [
'validation_groups' => ['Default'],
],
],
];
}
```
```php
// in src/MyCompany/MyBundle/Form/CreateVehicleFlow.php
public function getFormOptions($step, array $options = []) {
$options = parent::getFormOptions($step, $options);
$formData = $this->getFormData();
if ($step === 2) {
$options['numberOfWheels'] = $formData->getNumberOfWheels();
}
return $options;
}
```
--------------------------------
### Configure Doctrine Storage for FormFlow
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Set up database-backed storage for flow data to support multi-server environments.
```yaml
# config/packages/craue_form_flow.yaml
services:
# Storage key generator (uses user session + request for unique keys)
Craue\FormFlowBundle\Storage\UserSessionStorageKeyGenerator:
arguments:
- '@security.token_storage'
- '@request_stack'
# Doctrine storage using DBAL connection
Craue\FormFlowBundle\Storage\DoctrineStorage:
arguments:
- '@doctrine.dbal.default_connection'
- '@Craue\FormFlowBundle\Storage\UserSessionStorageKeyGenerator'
# Data manager with Doctrine storage
app.form_flow.data_manager.doctrine:
class: Craue\FormFlowBundle\Storage\DataManager
arguments:
- '@Craue\FormFlowBundle\Storage\DoctrineStorage'
```
```yaml
# config/services.yaml
services:
App\Form\CreateVehicleFlow:
autoconfigure: false
calls:
- [setDataManager, ['@app.form_flow.data_manager.doctrine']]
- [setFormFactory, ['@form.factory']]
- [setRequestStack, ['@request_stack']]
- [setEventDispatcher, ['@event_dispatcher']]
```
--------------------------------
### Create form type classes
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Define individual form steps by extending AbstractType. Ensure getBlockPrefix returns a unique identifier for each step.
```php
// src/MyCompany/MyBundle/Form/CreateVehicleStep1Form.php
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
class CreateVehicleStep1Form extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$validValues = [2, 4];
$builder->add('numberOfWheels', ChoiceType::class, [
'choices' => array_combine($validValues, $validValues),
'placeholder' => '',
]);
}
public function getBlockPrefix() {
return 'createVehicleStep1';
}
}
```
```php
// src/MyCompany/MyBundle/Form/CreateVehicleStep2Form.php
use MyCompany\MyBundle\Form\Type\VehicleEngineType;
use Symfony\
Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class CreateVehicleStep2Form extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('engine', VehicleEngineType::class, [
'placeholder' => '',
]);
}
public function getBlockPrefix() {
return 'createVehicleStep2';
}
}
```
--------------------------------
### Define steps configuration in loadStepsConfig
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Defines the steps of the flow, including form types, labels, and skip logic.
```php
protected function loadStepsConfig() {
return [
[
'form_type' => CreateVehicleStep1Form::class,
],
[
'form_type' => CreateVehicleStep2Form::class,
'skip' => true,
],
[
],
];
}
```
```php
protected function loadStepsConfig() {
return [
1 =>[
'label' => 'wheels',
'form_type' => CreateVehicleStep1Form::class,
],
2 => [
'label' => StepLabel::createCallableLabel(function() { return 'engine'; })
'form_type' => CreateVehicleStep2Form::class,
'form_options' => [
'validation_groups' => ['Default'],
],
'skip' => function($estimatedCurrentStepNumber, FormFlowInterface $flow) {
return $estimatedCurrentStepNumber > 1 && !$flow->getFormData()->canHaveEngine();
},
],
3 => [
'label' => 'confirmation',
],
];
}
```
--------------------------------
### Simplify loadStepsConfig in Flow
Source: https://github.com/craue/craueformflowbundle/blob/master/UPGRADE-2.1.md
The 'type' option is no longer required for empty steps to prevent exceptions.
```php
protected function loadStepsConfig() {
return array(
// ...
array(
'label' => 'confirmation',
'type' => $this->formType, // needed to avoid InvalidOptionsException regarding option 'flowStep'
),
);
}
```
```php
protected function loadStepsConfig() {
return array(
// ...
array(
'label' => 'confirmation',
),
);
}
```
--------------------------------
### Configure File Upload Handling
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Enable and configure file upload handling in a form flow. Set custom temporary directories for uploaded files using the $handleFileUploadsTempDir property.
```php
'Document Type', 'form_type' => DocumentTypeForm::class],
['label' => 'Upload File', 'form_type' => FileUploadForm::class],
['label' => 'Metadata', 'form_type' => MetadataForm::class],
['label' => 'Confirm', 'form_type' => ConfirmForm::class],
];
}
}
```
--------------------------------
### Create a Form Type Class (Approach A)
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Define a single form type that handles fields for different steps using the `flow_step` option. This approach is suitable for turning existing forms into flows.
```php
// src/MyCompany/MyBundle/Form/CreateVehicleForm.php
use MyCompany\MyBundle\Form\Type\VehicleEngineType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
class CreateVehicleForm extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
switch ($options['flow_step']) {
case 1:
$validValues = [2, 4];
$builder->add('numberOfWheels', ChoiceType::class, [
'choices' => array_combine($validValues, $validValues),
'placeholder' => '',
]);
break;
case 2:
// This form type is not defined in the example.
$builder->add('engine', VehicleEngineType::class, [
'placeholder' => '',
]);
break;
}
}
public function getBlockPrefix() {
return 'createVehicle';
}
}
```
--------------------------------
### Create a Flow Class (Approach B)
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Define a flow class where each step is associated with a distinct form type. This approach facilitates form type reusability.
```php
// src/MyCompany/MyBundle/Form/CreateVehicleFlow.php
use Craue\FormFlowBundle\Form\FormFlow;
use Craue\FormFlowBundle\Form\FormFlowInterface;
use MyCompany\MyBundle\Form\CreateVehicleStep1Form;
use MyCompany\MyBundle\Form\CreateVehicleStep2Form;
class CreateVehicleFlow extends FormFlow {
protected function loadStepsConfig() {
return [
[
'label' => 'wheels',
'form_type' => CreateVehicleStep1Form::class,
],
[
'label' => 'engine',
'form_type' => CreateVehicleStep2Form::class,
'skip' => function($estimatedCurrentStepNumber, FormFlowInterface $flow) {
return $estimatedCurrentStepNumber > 1 && !$flow->getFormData()->canHaveEngine();
},
],
[
'label' => 'confirmation',
],
];
}
}
```
--------------------------------
### Define Dynamic Form Options
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Override getFormOptions to inject data from previous steps into the current step's form configuration.
```php
'Category', 'form_type' => CategoryForm::class],
['label' => 'Product', 'form_type' => ProductForm::class],
['label' => 'Quantity', 'form_type' => QuantityForm::class],
];
}
public function getFormOptions($step, array $options = []): array
{
$options = parent::getFormOptions($step, $options);
$formData = $this->getFormData();
if ($step === 2) {
// Pass selected category to product form for filtering
$options['selected_category'] = $formData->getCategory();
}
if ($step === 3) {
// Pass selected product for stock validation
$options['selected_product'] = $formData->getProduct();
$options['max_quantity'] = $formData->getProduct()?->getStockQuantity() ?? 100;
}
return $options;
}
}
```
--------------------------------
### Configure DoctrineStorage for form flow
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Configures the bundle to persist form data in the database instead of the session.
```yaml
# config/packages/craue_form_flow.yaml
services:
Craue\FormFlowBundle\Storage\UserSessionStorageKeyGenerator:
arguments: [ '@security.token_storage', '@request_stack' ]
Craue\FormFlowBundle\Storage\DoctrineStorage:
arguments: [ '@doctrine.dbal.default_connection', '@Craue\FormFlowBundle\Storage\UserSessionStorageKeyGenerator' ]
myCompany.form.flow.storage.doctrine_storage:
class: 'Craue\FormFlowBundle\Storage\DataManager'
arguments: [ '@Craue\FormFlowBundle\Storage\DoctrineStorage' ]
```
```yaml
# config/services.yaml
services:
myCompany.form.flow.createVehicle:
autoconfigure: false
calls:
- [ setDataManager, [ '@myCompany.form.flow.storage.doctrine_storage'] ]
- [ setFormFactory, [ '@form.factory' ] ]
- [ setRequestStack, [ '@request_stack' ] ]
- [ setEventDispatcher, [ '@?event_dispatcher' ] ]
```
--------------------------------
### Configure Dynamic Step Navigation
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Enable dynamic navigation in the FormFlow class and clean up URL parameters in the Twig template.
```php
'Basic Info', 'form_type' => Step1Form::class],
['label' => 'Details', 'form_type' => Step2Form::class],
['label' => 'Review', 'form_type' => Step3Form::class],
];
}
}
```
```twig
{# Template with dynamic navigation parameter cleanup #}
{{ form_start(form, {
'action': path(app.request.attributes.get('_route'),
app.request.query.all | craue_removeDynamicStepNavigationParameters(flow))
}) }}
{# form content #}
{{ form_end(form) }}
```
--------------------------------
### Create a Flow Class for Multi-Step Forms
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Define the steps, labels, form types, and skip logic for your form flow by extending FormFlow.
```php
'wheels',
'form_type' => CreateVehicleStep1Form::class,
],
[
'label' => 'engine',
'form_type' => CreateVehicleStep2Form::class,
'form_options' => [
'validation_groups' => ['Default', 'engine_validation'],
],
'skip' => function ($estimatedCurrentStepNumber, FormFlowInterface $flow) {
// Skip engine step if vehicle has only 2 wheels (bicycle)
return $estimatedCurrentStepNumber > 1
&& $flow->getFormData()->getNumberOfWheels() === 2;
},
],
[
'label' => 'confirmation',
// No form_type means an empty confirmation form
],
];
}
}
```
--------------------------------
### Enable dynamic step navigation
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Enable dynamic navigation in the flow class and update the form template to handle navigation parameters.
```php
// in src/MyCompany/MyBundle/Form/CreateVehicleFlow.php
class CreateVehicleFlow extends FormFlow {
protected $allowDynamicStepNavigation = true;
// ...
}
```
```twig
{{ form_start(form, {'action': path(app.request.attributes.get('_route'),
app.request.query.all | craue_removeDynamicStepNavigationParameters(flow))}) }}
```
--------------------------------
### Migrate step description block usage
Source: https://github.com/craue/craueformflowbundle/blob/master/UPGRADE-2.0.md
Update template block calls from the old naming convention to the new one.
```twig
{{ block('craue_flow_stepDescription') }}
```
```twig
{{ block('craue_flow_stepLabel') }}
```
--------------------------------
### Enable CraueFormFlowBundle in Symfony (3.4)
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
For Symfony versions prior to 3.4, register the bundle manually in the AppKernel.php file.
```php
// in app/AppKernel.php
public function registerBundles() {
$bundles = [
// ...
new Craue\FormFlowBundle\CraueFormFlowBundle(),
];
// ...
}
```
--------------------------------
### Controller Action for Multi-Step Form
Source: https://context7.com/craue/craueformflowbundle/llms.txt
This controller action binds form data to the flow, handles form submission, and manages step transitions. It initializes the form data, creates the form for the current step, and processes valid submissions.
```php
bind($formData);
// Create the form for the current step
$form = $flow->createForm();
// Check if the form is valid (handles request internally)
if ($flow->isValid($form)) {
// Save the current step's data
$flow->saveCurrentStepData($form);
// Try to advance to the next step
if ($flow->nextStep()) {
// Not finished yet, create form for the next step
$form = $flow->createForm();
} else {
// Flow finished - persist the data
$em->persist($formData);
$em->flush();
// Clear flow data from session
$flow->reset();
$this->addFlash('success', 'Vehicle created successfully!');
return $this->redirectToRoute('vehicle_list');
}
}
return $this->render('vehicle/create.html.twig', [
'form' => $form->createView(),
'flow' => $flow,
'formData' => $formData,
]);
}
}
```
--------------------------------
### Register Flow Service with Autoconfiguration (YAML)
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Register your flow class as a Symfony service using autoconfiguration in `services.yaml`. This is the recommended approach for service registration.
```yaml
# config/services.yaml
services:
# With autoconfiguration (recommended)
App\Form\CreateVehicleFlow:
autoconfigure: true
```
--------------------------------
### Create a form template
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Use the flow variable to conditionally render form steps and include built-in templates for navigation.
```twig
{# in src/MyCompany/MyBundle/Resources/views/Vehicle/createVehicle.html.twig #}
Steps:
{% include '@CraueFormFlow/FormFlow/stepList.html.twig' %}
When selecting four wheels you have to choose the engine in the next step.
{{ form_row(form.numberOfWheels) }}
{% endif %}
{{ form_rest(form) }}
{% include '@CraueFormFlow/FormFlow/buttons.html.twig' %}
{{ form_end(form) }}
```
--------------------------------
### Configure file upload handling
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Disable automatic file upload handling or specify a custom temporary directory in the flow class.
```php
// in src/MyCompany/MyBundle/Form/CreateVehicleFlow.php
class CreateVehicleFlow extends FormFlow {
protected $handleFileUploads = false;
// ...
}
```
```php
// in src/MyCompany/MyBundle/Form/CreateVehicleFlow.php
class CreateVehicleFlow extends FormFlow {
protected $handleFileUploadsTempDir = '/path/for/flow/uploads';
// ...
}
```
--------------------------------
### Migrate createForm options
Source: https://github.com/craue/craueformflowbundle/blob/master/UPGRADE-3.0.md
Use setGenericFormOptions instead of passing options directly to createForm.
```php
$flow->bind($formData);
$form = $flow->createForm(array('action' => 'targetUrl'));
```
```php
$flow->setGenericFormOptions(array('action' => 'targetUrl'));
$flow->bind($formData);
$form = $flow->createForm();
```
--------------------------------
### Update buildForm to use flow_step
Source: https://github.com/craue/craueformflowbundle/blob/master/UPGRADE-2.1.md
Switch from using the deprecated 'flowStep' option to the new 'flow_step' option in buildForm.
```php
public function buildForm(FormBuilderInterface $builder, array $options) {
switch ($options['flowStep']) {
// ...
}
}
```
```php
public function buildForm(FormBuilderInterface $builder, array $options) {
switch ($options['flow_step']) {
// ...
}
}
```
--------------------------------
### Update Form Flow Step Configuration
Source: https://github.com/craue/craueformflowbundle/blob/master/UPGRADE-2.0.md
Refactor form flow configuration by replacing `maxSteps` and `loadStepDescriptions` with `setFormType` and `loadStepsConfig`. The `getFormOptions` method now accepts a `flowStep` option.
```php
protected $maxSteps = 3;
protected function loadStepDescriptions() {
return array(
'wheels',
'engine',
'confirmation',
);
}
```
```php
use Symfony\Component\Form\FormTypeInterface;
/**
* @var FormTypeInterface
*/
protected $formType;
public function setFormType(FormTypeInterface $formType) {
$this->formType = $formType;
}
protected function loadStepsConfig() {
return array(
array(
'label' => 'wheels',
'type' => $this->formType,
),
array(
'label' => 'engine',
'type' => $this->formType,
),
array(
'label' => 'confirmation',
'type' => $this->formType,
),
);
}
public function getFormOptions($step, array $options = array()) {
$options = parent::getFormOptions($step, $options);
$options['flowStep'] = $step;
return $options;
}
```
--------------------------------
### Register Flow Service with Autoconfiguration (XML)
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Register your flow class as a Symfony service using autoconfiguration in `services.xml`. This is an alternative to YAML configuration.
```xml
```
--------------------------------
### Migrate step description block definition
Source: https://github.com/craue/craueformflowbundle/blob/master/UPGRADE-2.0.md
Update template block definitions and variable access from stepDescription to stepLabel.
```twig
{% block craue_flow_stepDescription %}
{{ stepDescription | trans }}
{% endblock %}
```
```twig
{% block craue_flow_stepLabel %}
{{ stepLabel | trans }}
{% endblock %}
```
--------------------------------
### Implement Event Subscribers in Flow Class
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Subscribe to various form flow events by implementing `EventSubscriberInterface` and defining the `getSubscribedEvents` method. Ensure to manage event dispatcher subscriptions correctly, especially when autoconfiguration is used.
```php
// in src/MyCompany/MyBundle/Form/CreateVehicleFlow.php
use Craue\FormFlowBundle\Event\GetStepsEvent;
use Craue\FormFlowBundle\Event\PostBindFlowEvent;
use Craue\FormFlowBundle\Event\PostBindRequestEvent;
use Craue\FormFlowBundle\Event\PostBindSavedDataEvent;
use Craue\FormFlowBundle\Event\PostValidateEvent;
use Craue\FormFlowBundle\Event\PreBindEvent;
use Craue\FormFlowBundle\Form\FormFlowEvents;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class CreateVehicleFlow extends FormFlow implements EventSubscriberInterface {
/**
* This method is only needed when _not_ using autoconfiguration. If it's there even with autoconfiguration enabled,
* the `removeSubscriber` call ensures that subscribed events won't occur twice.
* (You can remove the `removeSubscriber` call if you'll definitely never use autoconfiguration for that flow.)
*/
public function setEventDispatcher(EventDispatcherInterface $dispatcher) {
parent::setEventDispatcher($dispatcher);
$dispatcher->removeSubscriber($this);
$dispatcher->addSubscriber($this);
}
public static function getSubscribedEvents() {
return [
FormFlowEvents::PRE_BIND => 'onPreBind',
FormFlowEvents::GET_STEPS => 'onGetSteps',
FormFlowEvents::POST_BIND_SAVED_DATA => 'onPostBindSavedData',
FormFlowEvents::POST_BIND_FLOW => 'onPostBindFlow',
FormFlowEvents::POST_BIND_REQUEST => 'onPostBindRequest',
FormFlowEvents::POST_VALIDATE => 'onPostValidate',
];
}
public function onPreBind(PreBindEvent $event) {
// ...
}
public function onGetSteps(GetStepsEvent $event) {
// ...
}
public function onPostBindSavedData(PostBindSavedDataEvent $event) {
// ...
}
public function onPostBindFlow(PostBindFlowEvent $event) {
// ...
}
public function onPostBindRequest(PostBindRequestEvent $event) {
// ...
}
public function onPostValidate(PostValidateEvent $event) {
// ...
}
// ...
}
```
--------------------------------
### Update flow instance links
Source: https://github.com/craue/craueformflowbundle/blob/master/UPGRADE-3.0.md
Include the instance parameter in flow links to support concurrent instances of the same flow.
```twig
continue creating a topic
```
```twig
continue creating a topic
```
--------------------------------
### Remove flowStep from setDefaultOptions
Source: https://github.com/craue/craueformflowbundle/blob/master/UPGRADE-2.1.md
Manual definition of 'flowStep' in setDefaultOptions is no longer necessary.
```php
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'flowStep' => null,
// ...
));
}
```
```php
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
// ...
));
}
```
--------------------------------
### Handle Redirect in Controller Action
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Implement the redirect logic in your controller action. This involves checking if a redirect is needed after form submission and constructing the redirect response.
```php
// in src/MyCompany/MyBundle/Controller/VehicleController.php
public function createVehicleAction() {
// ...
$flow->bind($formData);
$form = $submittedForm = $flow->createForm();
if ($flow->isValid($submittedForm)) {
$flow->saveCurrentStepData($submittedForm);
// ...
}
if ($flow->redirectAfterSubmit($submittedForm)) {
$request = $this->getRequest();
$params = $this->get('craue_formflow_util')->addRouteParameters(array_merge($request->query->all(),
$request->attributes->get('_route_params')), $flow);
return $this->redirectToRoute($request->attributes->get('_route'), $params);
}
// ...
// return ...
}
```
--------------------------------
### Register flow as a service
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Register the flow class as a service using autoconfiguration or by inheriting from the parent service.
```xml
```
```yaml
services:
myCompany.form.flow.createVehicle:
class: MyCompany\MyBundle\Form\CreateVehicleFlow
autoconfigure: true
```
```xml
```
```yaml
services:
myCompany.form.flow.createVehicle:
class: MyCompany\MyBundle\Form\CreateVehicleFlow
parent: craue.form.flow
```
--------------------------------
### Apply Generic Form Options
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Use setGenericFormOptions in the controller to apply shared configuration attributes to every step in the flow.
```php
setGenericFormOptions([
'action' => $this->generateUrl('vehicle_create'),
'attr' => ['class' => 'multi-step-form', 'novalidate' => 'novalidate'],
]);
$flow->bind($formData);
$form = $flow->createForm();
// ... rest of the controller logic
}
```
--------------------------------
### Dynamic Step Labels in Form Flow
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Use StepLabel::createCallableLabel to define dynamic step names based on form data. This allows for labels that change based on conditions like user login status or cart contents.
```php
'Cart Review',
'form_type' => CartReviewForm::class,
],
[
'label' => StepLabel::createCallableLabel(function () {
// Dynamic label based on whether user is logged in
return $this->getFormData()->isGuestCheckout()
? 'Guest Information'
: 'Shipping Address';
}),
'form_type' => AddressForm::class,
],
[
'label' => StepLabel::createCallableLabel(function () {
$items = $this->getFormData()->getCartItems()->count();
return sprintf('Payment (%d items)', $items);
}),
'form_type' => PaymentForm::class,
],
];
}
}
```
--------------------------------
### Implement EventSubscriberInterface in FormFlow
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Subscribe to various FormFlow lifecycle events by implementing EventSubscriberInterface and registering the class as a subscriber in the dispatcher.
```php
removeSubscriber($this);
$dispatcher->addSubscriber($this);
}
public static function getSubscribedEvents(): array
{
return [
FormFlowEvents::PRE_BIND => 'onPreBind',
FormFlowEvents::GET_STEPS => 'onGetSteps',
FormFlowEvents::POST_BIND_SAVED_DATA => 'onPostBindSavedData',
FormFlowEvents::POST_BIND_FLOW => 'onPostBindFlow',
FormFlowEvents::POST_BIND_REQUEST => 'onPostBindRequest',
FormFlowEvents::POST_VALIDATE => 'onPostValidate',
FormFlowEvents::PREVIOUS_STEP_INVALID => 'onPreviousStepInvalid',
FormFlowEvents::FLOW_EXPIRED => 'onFlowExpired',
];
}
public function onPreBind(PreBindEvent $event): void
{
// Called before the flow is bound to form data
// Useful for initialization logic
}
public function onGetSteps(GetStepsEvent $event): void
{
// Dynamically modify steps before they're loaded
// $event->setSteps($customSteps);
// $event->stopPropagation();
}
public function onPostBindSavedData(PostBindSavedDataEvent $event): void
{
// Called after each step's saved data is applied to form data
$stepNumber = $event->getStepNumber();
$formData = $event->getFormData();
}
public function onPostBindFlow(PostBindFlowEvent $event): void
{
// Called after the flow is fully bound
$formData = $event->getFormData();
}
public function onPostBindRequest(PostBindRequestEvent $event): void
{
// Called after the current request is bound to the form
$stepNumber = $event->getStepNumber();
}
public function onPostValidate(PostValidateEvent $event): void
{
// Called after form validation succeeds
$formData = $event->getFormData();
// Perform additional validation or processing
}
public function onPreviousStepInvalid(PreviousStepInvalidEvent $event): void
{
// Called when revalidation of a previous step fails
$invalidStepNumber = $event->getInvalidStepNumber();
}
public function onFlowExpired(FlowExpiredEvent $event): void
{
// Called when the flow has expired (session data lost)
$form = $event->getForm();
}
protected function loadStepsConfig(): array
{
return [
['label' => 'Step 1', 'form_type' => Step1Form::class],
['label' => 'Step 2', 'form_type' => Step2Form::class],
];
}
}
```
--------------------------------
### Update flow reset links
Source: https://github.com/craue/craueformflowbundle/blob/master/UPGRADE-3.0.md
Directly link to the flow action instead of a reset action.
```twig
create a topic
```
```twig
create a topic
```
--------------------------------
### Render Multi-Step Form in Twig
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Use this template structure to render step indicators, form fields based on the current step, and navigation buttons.
```twig
{# templates/vehicle/create.html.twig #}
{% extends 'base.html.twig' %}
{% block stylesheets %}
{{ parent() }}
{% endblock %}
{% block body %}
{% endif %}
{{ form_rest(form) }}
{# Render navigation buttons #}
{% include '@CraueFormFlow/FormFlow/buttons.html.twig' with {
craue_formflow_button_class_last: 'btn btn-primary',
craue_formflow_button_class_back: 'btn btn-secondary',
craue_formflow_button_class_reset: 'btn btn-outline-danger',
} %}
{{ form_end(form) }}
{% endblock %}
```
--------------------------------
### Enable Redirect After Submit in Flow Class
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Set the `allowRedirectAfterSubmit` property to `true` in your flow class to enable redirects after a step is submitted. This requires manual handling in the controller.
```php
// in src/MyCompany/MyBundle/Form/CreateVehicleFlow.php
class CreateVehicleFlow extends FormFlow {
protected $allowRedirectAfterSubmit = true;
// ...
}
```
--------------------------------
### Create a form flow action in a Symfony controller
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Handles the form flow lifecycle, including binding data, validating steps, and persisting data upon completion.
```php
// in src/MyCompany/MyBundle/Controller/VehicleController.php
public function createVehicleAction() {
$formData = new Vehicle(); // Your form data class. Has to be an object, won't work properly with an array.
$flow = $this->get('myCompany.form.flow.createVehicle'); // must match the flow's service id
$flow->bind($formData);
// form of the current step
$form = $flow->createForm();
if ($flow->isValid($form)) {
$flow->saveCurrentStepData($form);
if ($flow->nextStep()) {
// form for the next step
$form = $flow->createForm();
} else {
// flow finished
$em = $this->getDoctrine()->getManager();
$em->persist($formData);
$em->flush();
$flow->reset(); // remove step data from the session
return $this->redirectToRoute('home'); // redirect when done
}
}
return $this->render('@MyCompanyMy/Vehicle/createVehicle.html.twig', [
'form' => $form->createView(),
'flow' => $flow,
]);
}
```
--------------------------------
### Define a Step Form Type
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Create a Symfony form type for a specific step in the flow, defining its fields and options.
```php
add('numberOfWheels', ChoiceType::class, [
'choices' => array_combine($validValues, $validValues),
'placeholder' => 'Select number of wheels',
'label' => 'Number of Wheels',
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => \App\Entity\Vehicle::class,
]);
}
public function getBlockPrefix(): string
{
return 'createVehicleStep1';
}
}
```
--------------------------------
### Enable Redirect After Submit (PRG Pattern)
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Implement the Post/Redirect/Get pattern to prevent duplicate form submissions on page refresh. Set $allowRedirectAfterSubmit to true in your FormFlow configuration.
```php
'Step 1', 'form_type' => Step1Form::class],
['label' => 'Step 2', 'form_type' => Step2Form::class],
];
}
}
```
--------------------------------
### Enable CraueFormFlowBundle in Symfony
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Register the bundle in your Symfony application's configuration. This is required for the bundle to be active.
```php
// in config/bundles.php
return [
// ...
Craue\FormFlowBundle\CraueFormFlowBundle::class => ['all' => true],
];
```
--------------------------------
### Display Flow Progress in Twig Templates
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Utilize these Twig expressions to display the current step, total steps, and completion status within your templates. You can also iterate through steps to show their labels and apply CSS classes based on their status.
```twig
{# In Twig templates #}
Step {{ flow.getCurrentStepNumber() }} of {{ flow.getStepCount() }}
```
--------------------------------
### Customize Navigation Buttons
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Override default button labels and CSS classes by passing configuration parameters to the buttons template.
```twig
{# Custom button configuration #}
{% include '@CraueFormFlow/FormFlow/buttons.html.twig' with {
craue_formflow_button_label_last: 'Continue',
craue_formflow_button_label_finish: 'Submit Vehicle',
craue_formflow_button_label_next: 'Next Step',
craue_formflow_button_label_back: 'Go Back',
craue_formflow_button_label_reset: 'Start Over',
craue_formflow_button_class_last: 'btn btn-lg btn-success',
craue_formflow_button_class_finish: 'btn btn-lg btn-primary',
craue_formflow_button_class_next: 'btn btn-lg btn-info',
craue_formflow_button_class_back: 'btn btn-secondary',
craue_formflow_button_class_reset: 'btn btn-danger',
craue_formflow_button_render_reset: false,
} %}
```
--------------------------------
### Configure Validation Groups Per Step
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Customize validation groups for individual steps or disable validation entirely by overriding the validation group prefix and configuring form options.
```php
'Account',
'form_type' => AccountForm::class,
// Will use validation group: registration_step1
],
[
'label' => 'Profile',
'form_type' => ProfileForm::class,
'form_options' => [
// Add custom validation groups alongside the auto-generated one
'validation_groups' => ['Default', 'profile_extra'],
],
],
[
'label' => 'Preferences',
'form_type' => PreferencesForm::class,
'form_options' => [
// Disable validation entirely for this step
'validation_groups' => false,
],
],
];
}
}
```
--------------------------------
### Register Flow Service with Explicit Parent (YAML)
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Alternatively, register your flow class as a Symfony service by explicitly setting its parent service in `services.yaml`.
```yaml
# config/services.yaml
services:
# Alternative: with explicit parent service
App\Form\CreateVehicleFlow:
parent: craue.form.flow
```
--------------------------------
### Set generic form options
Source: https://github.com/craue/craueformflowbundle/blob/master/README.md
Use setGenericFormOptions in the controller to apply common options to all steps in the flow.
```php
// in src/MyCompany/MyBundle/Controller/VehicleController.php
public function createVehicleAction() {
// ...
$flow->setGenericFormOptions(['action' => 'targetUrl']);
$flow->bind($formData);
$form = $flow->createForm();
// ...
}
```
--------------------------------
### Rename step config option type to form_type
Source: https://github.com/craue/craueformflowbundle/blob/master/UPGRADE-3.0.md
Update the loadStepsConfig method to use form_type instead of the deprecated type option.
```php
protected function loadStepsConfig() {
return array(
array(
'type' => $this->formType,
),
// ...
);
}
```
```php
protected function loadStepsConfig() {
return array(
array(
'form_type' => $this->formType,
),
// ...
);
}
```
--------------------------------
### Access Flow State in PHP Controller
Source: https://context7.com/craue/craueformflowbundle/llms.txt
Use these methods to access and check the current state of the form flow within a PHP controller. They provide information about step numbers, labels, completion status, and form data.
```php
getCurrentStepNumber(); // Current step (1-based)
$flow->getStepCount(); // Total number of steps
$flow->getFirstStepNumber(); // First visible step (may be > 1 if skipped)
$flow->getLastStepNumber(); // Last visible step
$flow->getCurrentStepLabel(); // Label of current step
$flow->getStepLabels(); // Array of all step labels
$flow->isStepDone(2); // Check if step 2 is completed
$flow->isStepSkipped(2); // Check if step 2 is skipped
$flow->getSteps(); // Get all Step objects
$flow->getStep(2); // Get Step object for step 2
$flow->getStepsDone(); // Get completed Step objects
$flow->getStepsRemaining(); // Get remaining Step objects
$flow->getStepsDoneCount(); // Count of completed steps
$flow->getStepsRemainingCount(); // Count of remaining steps
$flow->getFormData(); // Get the bound form data object
$flow->reset(); // Clear all step data and restart
```
--------------------------------
### Remove manual flowStep option in getFormOptions
Source: https://github.com/craue/craueformflowbundle/blob/master/UPGRADE-2.1.md
The 'flowStep' option is now set automatically, allowing for the removal of manual overrides in getFormOptions.
```php
public function getFormOptions($step, array $options = array()) {
$options = parent::getFormOptions($step, $options);
$options['flowStep'] = $step;
// ...
return $options;
}
```
```php
public function getFormOptions($step, array $options = array()) {
$options = parent::getFormOptions($step, $options);
// ...
return $options;
}
```