how to split long symfony form in multiple pages?
Asked Answered
A

3

12

I want to create a form for an entity which has a many attributes. To ensure the ease of data entry, I want to split that form in multiple pages (for example 2 or 3 pages). Let's take the ad entity example:

  1. In page 1, the user will enter the ad text
  2. In page 2, the user will enter his contact
  3. In page 3, the user will provide the (X,Y) position of the ad

This split will require saving the available data (inserting in the database) in the 1st page before moving to the next page. Unfortunately, This is not possible due to constraints.

The question is: Is any documentations or any examples that solve this issue?

If no documentation is available, do you think it is better to split my entity in n entities in order to have one entity per page?

Thanks for you help.

Apotheosize answered 21/1, 2014 at 9:57 Comment(0)
G
12

You probably should use CraueFormFlowBundle. It provides facilities for building multi-step forms.

You can create one form type for an entire flow, or one form type per step.

It's very easy to setup. Everything is explained here.

Gluck answered 21/1, 2014 at 10:59 Comment(5)
Thank you very much. Seems to be a great solution! :)Apotheosize
Absolutely perfect! Thanks again!Apotheosize
I doubt if a Bundle could be a great solution. Personally often prefer coding as a good solution.Socage
Not good solution there is still problem with mapped => false fieldsCongregation
@Pmpr 3rd party libraries are great if they're well maintained and decent. Why spend hours/days+ writing your own in house code when someone else has already done it and been through the niggles? Just use a service class in your own code base which implements the 3rd party library. Then inject your own class throughout your code so it's easy to swap out one library for another (or your own if needed later)Crashland
D
12

You don't have to split your entity, but your form : create 3 forms, each containing the property needed from the ad entity.

You'll need to :

  • persist (and not flush) the $ad object at every step inside your controller
  • pass the $ad object as an argument when forwarding inside your controller
  • flush the $ad object in the last step

In pseudo-code, your controller would look like this :

public function newAdStep1() {
    new Ad() // New instance of $ad
    new formStep1($ad) // The first form containing only the ad text field

    // The form was filled, manage it...
    form->isValid()? {
        persist($ad); // Persist the first part of your ad object
        forward(newAdStep2, $ad) // Go on to step 2, your $ad object as an argument
    }

    // ... or display step1 to user
    createView createAdStep1.html.twig('form' => $form);
}

public function newAdStep2($ad) {
    new formStep2($ad); // Now the second form, containing the "contact" fields
    isValid ? {
        persist($ad)
        forward(newAdStep3, $ad)
    }
    createView createAdStep2($form, $ad); // Your $ad object needs to be sent to the view
}

public function newAdStep3($ad) {
    new formStep3($ad); // Third and last form, containing the (X,Y) fields
    isValid ? {
        $em->persist($ad);
        $em->flush(); // Your instance of $ad can be stored in database now
        return('success !');
    }
    return view createAdStep3($form, $ad);
}
Diver answered 21/1, 2014 at 10:49 Comment(5)
Thank you very much. I have to try you solution. I am little bit worried about the isValid().Apotheosize
I tried it, and isValid returns true after the createForm, so this solution may not be viable. However, the concept of using forwards is pretty nifty.Spiffy
It could be helpful if you revise your code snippet and replace it with real world codeSocage
Can you test your code, please ? I tried your solution with Symfony3 but the flush doesn't work in the last step. Can you help me here : #47650977Ultramicrometer
Now what if after let's say first step user's closes browser?Hardened
G
12

You probably should use CraueFormFlowBundle. It provides facilities for building multi-step forms.

You can create one form type for an entire flow, or one form type per step.

It's very easy to setup. Everything is explained here.

Gluck answered 21/1, 2014 at 10:59 Comment(5)
Thank you very much. Seems to be a great solution! :)Apotheosize
Absolutely perfect! Thanks again!Apotheosize
I doubt if a Bundle could be a great solution. Personally often prefer coding as a good solution.Socage
Not good solution there is still problem with mapped => false fieldsCongregation
@Pmpr 3rd party libraries are great if they're well maintained and decent. Why spend hours/days+ writing your own in house code when someone else has already done it and been through the niggles? Just use a service class in your own code base which implements the 3rd party library. Then inject your own class throughout your code so it's easy to swap out one library for another (or your own if needed later)Crashland
B
1

You could store all the submitted data in a session or temporary table, then persist it all together at the end. However, I do my best to avoid extra work like this.

I think it may well be that your form steps follow the order by which the constraints dictate.

Saying that, sometimes I think this kind of problem can be solved by making a better design or process decision. i.e. limit the number of questions or only ask the vital ones at first. Without knowing the ins and outs it's hard to know whether this can be done.

Botfly answered 21/1, 2014 at 10:36 Comment(1)
Session is not a good place, and will lead to session abuse. I would use a cache.Linnea

© 2022 - 2024 — McMap. All rights reserved.