1

I have a form (still not finished and is missing many fields) that is handled as a wizard with steps, in which fields from multiple entities are handled. This is the form itself:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
            ->add('tipo_tramite', 'entity', array(
                'class' => 'ComunBundle:TipoTramite',
                'property' => 'nombre',
                'required' => TRUE,
                'label' => "Tipo de Trámite",
                'query_builder' => function (EntityRepository $er) {
                    return $er->createQueryBuilder('q')
                            ->where('q.activo = :valorActivo')
                            ->setParameter('valorActivo', TRUE);
                }
            ))
            ->add('oficina_regional', 'entity', array(
                'class' => 'ComunBundle:OficinaRegional',
                'property' => 'nombre',
                'required' => TRUE,
                'label' => "Oficina Regional",
                'query_builder' => function (EntityRepository $er) {
                    return $er->createQueryBuilder('q')
                            ->where('q.activo = :valorActivo')
                            ->setParameter('valorActivo', TRUE);
                }
            ))
            ->add('procedencia_producto', 'entity', array(
                'class' => 'ComunBundle:ProcedenciaProducto',
                'property' => 'nombre',
                'required' => TRUE,
                'label' => "Procedencia del Producto"
            ))
            ->add('finalidad_producto', 'entity', array(
                'class' => 'ComunBundle:FinalidadProducto',
                'property' => 'nombre',
                'required' => TRUE,
                'label' => "Finalidad del Producto"
            ))
            ->add('condicion_producto', 'entity', array(
                'class' => 'ComunBundle:CondicionProducto',
                'property' => 'nombre',
                'required' => TRUE,
                'label' => "Condición del Producto"
            ))
            ->add('lote', 'integer', array(
                'required' => TRUE,
                'label' => "Tamaño del Lote"
            ))
            ->add('observaciones', 'text', array(
                'required' => FALSE,
                'label' => "Observaciones"
    ));

}

I have a question regarding how to handle the parameter data_class in this case so I do not have to do magic in the controller. When I say magic I mean the following:

public function empresaAction()
{
    $entity = new Empresa();
    $form = $this->createForm(new EmpresaFormType(), $entity);
    return array( 'entity' => $entity, 'form' => $form->createView() );

}

public function guardarEmpresaAction(Request $request)
{
    $em = $this->getDoctrine()->getManager();

    /** @var $userManager \FOS\UserBundle\Model\UserManagerInterface */
    $userManager = $this->container->get('fos_user.user_manager');
    /** @var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
    $dispatcher = $this->container->get('event_dispatcher');
    /** @var $mailer FOS\UserBundle\Mailer\MailerInterface */
    $mailer = $this->container->get('fos_user.mailer');

    $request_empresa = $request->get('empresa');
    $request_sucursal = $request->get('sucursal');
    $request_chkRif = $request->get('chkRif');

    $request_estado = $request_empresa[ 'estado' ];
    $request_municipio = $request->get('municipio');
    $request_ciudad = $request->get('ciudad');
    $request_parroquia = $request->get('parroquia');

    $user = $userManager->createUser();

    $event = new GetResponseUserEvent($user, $request);
    $dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, $event);

    if (null !== $event->getResponse())
    {
        return $event->getResponse();
    }

    $entity = new Empresa();
    $form = $this->createForm(new EmpresaFormType(), $entity);
    $form->handleRequest($request);

    $success = $url = $errors = "";

    if ($form->isValid())
    {
        if ($request_sucursal != NULL || $request_sucursal != "")
        {
            $padreEntity = $em->getRepository('UsuarioBundle:Empresa')->findOneBy(array( "padre" => $request_sucursal ));

            if (!$padreEntity)
            {
                $padreEntity = $em->getRepository('UsuarioBundle:Empresa')->findOneBy(array( "id" => $request_sucursal ));
            }

            if ($request_chkRif != NULL || $request_chkRif != "")
            {
                $rifUsuario = $request_empresa[ 'tipo_identificacion' ] . $request_empresa[ 'rif' ];
            }
            else
            {
                $originalRif = $padreEntity->getRif();
                $sliceRif = substr($originalRif, 10, 1);
                $rifUsuario = $originalRif . ($sliceRif === false ? 1 : $sliceRif + 1);
            }

            $entity->setPadre($padreEntity);
        }
        else
        {
            $rifUsuario = $request_empresa[ 'tipo_identificacion' ] . $request_empresa[ 'rif' ];
        }

        $user->setUsername($rifUsuario);
        $user->setRepresentativeName($request_empresa[ 'razon_social' ]);
        $user->setEmail($request_empresa[ 'usuario' ][ 'email' ]);
        $user->setPlainPassword($request_empresa[ 'usuario' ][ 'plainPassword' ][ 'first' ]);

        $pais = $em->getRepository('ComunBundle:Pais')->findOneBy(array( "id" => 23 ));
        $user->setPais($pais);

        $estado_id = $request_estado ? $request_estado : 0;
        $estado = $em->getRepository('ComunBundle:Estado')->findOneBy(array( "id" => $estado_id ));
        $user->setEstado($estado);

        $municipio_id = $request_municipio ? $request_municipio : 0;
        $municipio = $em->getRepository('ComunBundle:Municipio')->findOneBy(array( "id" => $municipio_id ));
        $user->setMunicipio($municipio);

        $ciudad_id = $request_ciudad ? $request_ciudad : 0;
        $ciudad = $em->getRepository('ComunBundle:Ciudad')->findOneBy(array( "id" => $ciudad_id ));
        $user->setCiudad($ciudad);

        $parroquia_id = $request_parroquia ? $request_parroquia : 0;
        $parroquia = $em->getRepository('ComunBundle:Parroquia')->findOneBy(array( "id" => $parroquia_id ));
        $user->setParroquia($parroquia);

        ...
    }
    else
    {
        $errors = $this->getFormErrors($form);
        $success = FALSE;
    }

    return new JsonResponse(array( 'success' => $success, 'errors' => $errors, 'redirect_to' => $url ));

}

Since the 'data_classonEmpresaFormTypeis set toUsuarioBundle\Entity\Empresa` then I need to handle any extra parameter as example above show with getter/setter and that's a lot of work for complex/big forms.

In the sample form, the fields tipo_tramite will persist in the class ComunBundle\Entity\Producto but the field oficina_regional will persist in the class ComunBundle\Entity\SolicitudUsuario and so with others who are not placed even here but they are in the form, in total should persist as 3 or 4 entities including relationships in many cases, how do you handle this?

I know there is CraueFormFlowBundle that maybe cover this process/flow but not sure if it is the solution to go.

Any advice?

doydoy44
  • 5,720
  • 4
  • 29
  • 45
ReynierPM
  • 17,594
  • 53
  • 193
  • 363

1 Answers1

1

Just worked with multi-step form few days ago. I think you need embedded forms here. Anyway can suggest you another nice bundle for creating wizard: SyliusFlowBundle. IMO it's more flexible and easier to understand than CraueFormFlowBundle.

BTW why do you want to store all in one form? We've used one form for each step, and I liked this approach.

Roman Kliuchko
  • 499
  • 4
  • 20
  • @roma-kliuchko I'm taking ideas is not a final decision I'm trying to get others experiences around this, it's possible for you to share at least the bundle where you use the bundle suggested by you? Will be nice have this as a study case – ReynierPM Oct 13 '14 at 21:08
  • sorry, can't share that code, it's private. But they have nice doc [here](http://docs.sylius.org/en/latest/bundles/SyliusFlowBundle/index.html). Hope it can help you to make decision. – Roman Kliuchko Oct 13 '14 at 21:23
  • @roma-kliuchko Following your suggestion I'm moving into SyliusFlowBundle but have a lot of doubts and docs isn't so good for developers, just a few lines and nothing else. So, you mention here you already did the same using this bundle, right? So my first question: where and how I instantiate/call the Scenario I'm creating? – ReynierPM Oct 14 '14 at 14:35
  • 1
    @ReynierPM, you shouldn't instantiate it directly. Just configure sylius.flow as service like this: `some_name.scenario.flow: class: path\to\your\scenario calls: - [ setContainer, [@service_container] ] tags: - { name: sylius.process.scenario, alias: some_alias }` and configure routing: `some_flow: resource: @SyliusFlowBundle/Resources/config/routing.yml prefix: /some_url` – Roman Kliuchko Oct 14 '14 at 15:30
  • sorry for formatting - can't find out how to make it better in comment. Except this config all you need is define your Scenario and Steps you needed. – Roman Kliuchko Oct 14 '14 at 15:35
  • @roma-kliuchko still not getting how I should invoke the form on my controller maybe my answer is wrong and should be something like, how do I invoke and render the form in a controller for later usage? Also I'm asking if it's possible to save (persist) each step and leave the process incomplete and then come back to the same form, edit the flow and have the same persisted data. – ReynierPM Oct 14 '14 at 17:06
  • @ReynierPM, with this bundle no need to define controller as usual. Instead you're defining steps which are controllers themselves (Sylius' ContainerAwareStep extends controller). In step you implement `displayAction` and `forwardAction`. Of course you can persist submitted data in `forwardAction`, using `$context->getStorage()->set()` or even write data to db with entity manager. – Roman Kliuchko Oct 14 '14 at 21:38
  • @roma-kliuchko ok, since you can't share the whole bundle can you share just a `forwardAction` just to see how did you manage things inside this function? – ReynierPM Oct 14 '14 at 21:44
  • yep, `public function forwardAction(ProcessContextInterface $context) { $form = $this->getStepForm($context->getStorage()->get('some_name')); $form->handleRequest($context->getRequest()); if ($context->getRequest()->isMethod('POST') && $form->isValid()) { return $this->onFormValid($form, $context); } return $this->createView($form, $context); }` - here `getStepForm()` and `onFormValid` are abstract methods. First of them only creates form, second - can make some additional checks and if data is ok - saves it. – Roman Kliuchko Oct 14 '14 at 22:26
  • The simplest `onFormValid` maybe like this: `protected function onFormValid(Form $form, ProcessContextInterface $context) { $context->getStorage()->set('some_name', $form->getData()); return $this->complete(); }` - instead of setting data to storage you can persist form data with entity manager if needed – Roman Kliuchko Oct 14 '14 at 22:29
  • @roma-kliuchko I'm getting the whole thing now thx to you, as a related answer can you take a look to [this](http://stackoverflow.com/questions/26373355/customize-syliusflowbundle-render)? – ReynierPM Oct 15 '14 at 02:08