### 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' %}
{{ form_start(form) }} {{ form_errors(form) }} {% if flow.getCurrentStepNumber() == 1 %}
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 %}

Create Vehicle - Step {{ flow.getCurrentStepNumber() }} of {{ flow.getStepCount() }}

{# Render step list/progress indicator #}
{% include '@CraueFormFlow/FormFlow/stepList.html.twig' %}
{{ form_start(form) }} {{ form_errors(form) }} {# Render step-specific content #} {% if flow.getCurrentStepNumber() == 1 %}

{{ flow.getCurrentStepLabel() | trans }}

Select the number of wheels for your vehicle.

{{ form_row(form.numberOfWheels) }}
{% elseif flow.getCurrentStepNumber() == 2 %}

{{ flow.getCurrentStepLabel() | trans }}

{{ form_row(form.engineType) }} {{ form_row(form.engineCapacity) }}
{% elseif flow.getCurrentStepNumber() == 3 %}

Confirmation

Number of Wheels: {{ formData.numberOfWheels }}

{% if formData.engineType %}

Engine Type: {{ formData.engineType }}

{% endif %}
{% 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() }}

Current: {{ flow.getCurrentStepLabel() }}

Completed: {{ flow.getStepsDoneCount() }}, Remaining: {{ flow.getStepsRemainingCount() }}

``` -------------------------------- ### 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; } ```