2

These are my first steps on SF2. I want to set up a multi step form on an entity which contains other entities.

I've a form type (shortened)

class ApplicationFormType extends AbstractType
{
    protected $_step = 1;

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        if ($this->_step == 1) {
            $builder
                ->add('patient', new Type\PersonType())
                ->add('insurance', 'entity', array(
                    'class' => 'Acme\MainBundle\Entity\Insurance',
                ));
        } elseif ($this->_step == 2) {
            $nurse = new Type\PersonType();
            $nurse->requireBareData();
            $builder
                ->add('nurse', $nurse)
                ->add('nurse_type', 'entity', array(
                    'class' => 'Acme\MainBundle\Entity\NurseType',
                    'expanded' => true,
                    'multiple' => false,
                ))
                ->add('nursing_support', 'checkbox', array(
                    'required' => false,
                ));
        }
    }

    public function setStep($step)
    {
        $this->_step = $step;
    }

My controller looks like

public function assistAction() {
    $request = $this->getRequest();

    $step = $this->getSession()->get('assist_step', 1);
    if ($request->getMethod() != 'POST') {
        $step = 1;
        $this->getSession()->set('assist_data', NULL);
    }
    $this->getSession()->set('assist_step', $step);
    $application = new Application();
    $type = new ApplicationFormType();
    $type->setStep($step);

    $form = $this->createForm($type, $application, array(
        'validation_groups' => array($step == 1 ? 'FormStepOne' : 'FormStepTwo'),
    ));
    if ($request->getMethod() == 'POST') {
        $form->bindRequest($request);
        if ($form->isValid()) {
            if ($step == 1) {

                $data = $form->getData();
                $this->getSession()->set('assist_data', serialize($data));
                $this->getSession()->set('assist_step', ++$step);
                $type = new ApplicationFormType();
                $type->setStep($step);
                $form = $this->createForm($type, $application, array(
                    'validation_groups' => array('FormStepTwo'),
                ));

            } else {

                $step_1 = unserialize($this->getSession()->get('assist_data', ''));
                $data = $form->getData();
=>                $data->setPatient($step_1->getPatient());
=>                $data->setInsurance($step_1->getInsurance());
                $this->getSession()->set('assist_data', NULL);
                $this->getSession()->set('assist_step', 1);

            }
        }
    }

    return $this->render('Acme:Default:onlineassist.html.twig', array(
        'form' => $form->createView(),
        'step' => $step,
    ));
}

My question is, if I had to "copy" the properties of first form step seperately, like:

$data->setPatient($step_1->getPatient());
$data->setInsurance($step_1->getInsurance());

or can I merge the unserialized data from session with data from second form step? Or is there a completely different approach?

rabudde
  • 7,498
  • 6
  • 53
  • 91
  • What's the ultimate purpose of the multi-step form? You want to keep the submitted data (if it was valid) throughout the process and once the client reviews the data then you inject it in the DB, correct? – Thomas Potaire Mar 20 '13 at 05:50
  • @ThomasPotaire: Not exactly. The client doesn't review the data. As you can see (in FormType) the second step provides another entity fields to be filled by user. (And there would be a third step.) When the user completes all steps (btw: second one would be optional soon) the `Application` entity with all related entities should be persisted to DB. – rabudde Mar 20 '13 at 06:17

2 Answers2

1

Entities can be serialized and put in the session according to the response on stackoverflow.

If each form is a different type, I'd advise you to break apart the type in multiple types.

Here is how I envision it:

public function submitAction($step = 1)
{
    switch ($step) {
        case 1:
            $entity = new EntityOne();
            $form = $this->createForm(new EntityOneType(), $entity);
            $template = "template_one.html.twig";
            $redirect = $urlToStepTwo; // which redirects to this controller action with $step = 2
            break;
        case 2:
            $entity = new EntityTwo();
            $form = $this->createForm(new EntityTwoType(), $entity);
            $template = "template_two.html.twig";
            $redirect = $urlToStepThree; // which redirects to this controller action with $step = 3
            break;
        case 3:
            $entity = new EntityThird();
            $form = $this->createForm(new EntityThreeType(), $entity);
            $template = "template_three.html.twig";
            $redirect = $urlToConfirmPage; // which redirects to another controller action that unserialize all entities and save them in the DB
            break;
        default:
            throw $this->createNotFoundException();
            break;
    }

    $request = $this->get('request');
    $session = $this->get('session');

    if ($request->isMethod('post') === true) {

        $form->bind($request);

        if ($form->isValid() === true) {
            $session->set('entity_'. $step, serialize($entity));

            return $this->redirect($redirect);
        }
    }

    $params = array(
        'form'    => $form->createView(),
        'entity'  => $entity
    );

    return $this->render($template, $params);
}
Community
  • 1
  • 1
Thomas Potaire
  • 6,166
  • 36
  • 39
  • Your solution is good for seperate entities, surely. But I use a big entity for two of the three steps. The `ApplicationFormType` represents an `Application` entity. (BTW: your code is not SF2.2 conform, i.e. `bindRequest` vs `bind` and `getMethod()` vs `isMethod('post')`) – rabudde Mar 21 '13 at 09:56
  • You could create two form types that represents a set of data of the `Application` and fuse them afterwards (thanks for nothing the deprecated calls). – Thomas Potaire Mar 21 '13 at 14:57
  • That's exactly what I'm doing (have a look at my post). And that's exactly my question. How to merge these two steps. Could it be done with one statement in PHP or have I to set every property of the first step in the second step manually? – rabudde Mar 21 '13 at 18:41
0

Ok, found no other way than setting pre-step field seperately, like

$pre_step = unserialize($this->getSession()->get('assist_data', ''));
$data->setPatient($pre_step->getPatient());
$data->setInsurance($pre_step->getInsurance());
$data->setInsuranceNumber($pre_step->getInsuranceNumber());
$data->setInsuranceLevel($pre_step->getInsuranceLevel());

And that's what I've already done so far.

rabudde
  • 7,498
  • 6
  • 53
  • 91