📜 ⬆️ ⬇️

Symfony2 Step Form with SyliusFlowBundle

Sometimes when developing web applications other than a primitive blog or business card site, you may be faced with the need to create bulky forms — information about a user with multiple addresses, personal data, pet names , etc. Creating a step-by-step form can simplify the lives of users, but not always developers. In an attempt to help the latter, this post was written.

We will consider the bundle already mentioned in the open spaces of the e-commerce platform Sylius - SyliusFlowBundle . Why precisely he? The recommendation of a colleague who pained due to CraueFormFlowBundle's inflexibility, project documentation and sylius.org design put the internal scales on the side of the described solution. In general, let's go.

First you need to pull up the bundle itself:
composer require "sylius/flow-bundle" 

Next, edit the AppKernel.php:
 <?php // app/AppKernel.php public function registerBundles() { $bundles = array( new Sylius\Bundle\FlowBundle\SyliusFlowBundle(), // Other bundles... ); } 

In order for our form to start, you need to perform the following actions:
  1. Create the necessary steps - objects immenting the interface StepInterface, with a template for each of them.
  2. Link the steps to the script - interface ProcessScenarioInterface and declare it as a service
  3. Import routing from bundle

That's all you need to get a working step by step form. Now consider each of the steps in more detail.
')

Creating steps


As mentioned above, SyliusFlowBundle requires us to implement our StepInterface, for this it is convenient to use the kindly provided abstraction ControllerStep, which, as can be seen from the name, in turn inherits the symphon controller. The solution offers us to break a step into two actions - displayAction and forwardAction. The first obviously displays the form, the second processes the POST request and redirects the user to the next step. Most likely, your steps will have a lot of common code that can be put into its own abstract class. For example:
BaseStep
 abstract class BaseStep extends ControllerStep { const USER = 'user'; /** {@inheritdoc} */ public function displayAction(ProcessContextInterface $context) { return $this->createView( $this->getStepForm($context->getStorage()->get(self::USER)), $context ); } /** {@inheritdoc} */ public function forwardAction(ProcessContextInterface $context) { $form = $this->getStepForm($context->getStorage()->get(self::USER)); $form->handleRequest($context->getRequest()); if ($context->getRequest()->isMethod('POST') && $form->isValid()) { return $this->onFormValid($form, $context); } return $this->createView($form, $context); } /** * @param Form $form * @param ProcessContextInterface $context * * @return Response */ abstract protected function createView(Form $form, ProcessContextInterface $context); /** * @param mixed $data * * @return Form */ abstract protected function getStepForm($data = null); /** * @param Form $form * @param ProcessContextInterface $context * * @return mixed */ abstract protected function onFormValid(Form $form, ProcessContextInterface $context); } 


Here, in displayAction, we retrieve an object from the store (by default, session), get the form depending on the specific step, and render the page (the implementation will also need to be provided at a specific step). The only task is forwardAction data validation. Finally, take a look at the implementation of the step:
MainStep
 class MainStep extends BaseStep { /** {@inheritdoc} */ public function displayAction(ProcessContextInterface $context) { $context->getStorage()->remove(self::USER); return parent::displayAction($context); } /** {@inheritdoc} */ protected function createView(Form $form, ProcessContextInterface $context) { return $this->render('HospectAppBundle:Process:step.html.twig', [ 'form' => $form->createView(), 'context' => $context, ]); } /** {@inheritdoc} */ protected function getStepForm($data = null) { return $this->createForm(new MainInfoType(), $data); } /** {@inheritdoc} */ protected function onFormValid(Form $form, ProcessContextInterface $context) { $context->getStorage()->set(self::USER, $form->getData()); return $this->complete(); } } 


Here, the desired form is created predictably, a step-specific representation is rendered and, in the case of validity of the data, it is saved to the session. The last step should, of course, save the long-suffering object in your favorite permanent data store.

Linking steps to a script


At this stage we have several spherical steps cut off from the application. To change this you need to create a script. The script is the link of the general form and is responsible for such things as: the sequence of steps, the installation of parameters of the route common to all steps, the redirection after the successful completion of all steps, etc. However, in reality, there is not so much work here - the interface needs to implement only one method — build, which accepts only one parameter at the input — the builder with the methods familiar to it.
UserScenario
 class UserScenario extends ContainerAware implements ProcessScenarioInterface { /** {@inheritdoc} */ public function build(ProcessBuilderInterface $builder) { $builder ->add('main', new MainStep()) ->add('address', new AddressStep()) ->setRedirect('hospect_app_homepage'); } } 


In order for the system to “find out” about our script, you need to announce it as a service with the 'sylius.process.scenario' tag:
 sylius.scenario.flow: class: Hospect\AppBundle\Process\Scenario\UserScenario calls: - [ setContainer, [@service_container] ] tags: - { name: sylius.process.scenario, alias: user } 


Routing configuration


We are offered only three routes for the entire form, no matter how many steps there are. All of them have an obligatory parameter of scenarioAlias, being guided by which the necessary scenario pulls up. The routes 'sylius_flow_display' and 'sylius_flow_forward' require the presence of a second parameter called 'stepName'. That's all. Import looks like this:
 sylius_flow: resource: @SyliusFlowBundle/Resources/config/routing.yml prefix: / 


Links


Source: https://habr.com/ru/post/240909/


All Articles