Sometimes we need to split the form into several parts (pages), process them separately and then assemble them into one result.
This article describes methods and design patterns to do this.
The issue of splitting a form into multiple steps is very complex, especially if you want to do it well. I have encountered many approaches in my lifetime, which I will discuss here. Some approaches look appealing, but are naive and only work in specific cases. For each approach, I describe when it makes sense and which approaches don't make sense.
Usually the goal is to get the basic data from the first form on the first page, validate it, then save it "somewhere" and display the next page.
When the user gets to the last page, the overall form needs to be submitted and the inputs processed.
At each step, it's important to always carefully validate all the data and allow the user to skip back through the pages at will so that they can correct the data back when they encounter an error. In addition, if the form is to be rendered conditionally based on the data already obtained, this is a very demanding process.
We can either implement the individual forms ourselves in pure HTML and then handle the processing in PHP, or use ready-made solutions such as Nette forms.
Example from life:
Very often, novice programmers email me and ask seemingly simple questions for which there is a ready-made solution. For example, specifically about form processing in PHP.
I always recommend to skip manual processing altogether and use a ready-made solution instead. In reality, it is very complicated to correctly implement, for example, validation of the entered email and that 2 passwords within 2 fields match, while we want to redirect the user back to the pre-filled form according to his data and with error messages in case of an error.
Because people don't know they don't know they don't know and so instead of investing 1 hour of time in learning a ready-made solution to 99.99% of problems, they prefer to choose their own solution, which they spend dozens of hours debugging, and there are still cases where forms don't work, throw errors, have security vulnerabilities, and don't protect the data entered.
So the goal of this step is to implement several pages at different URLs that will contain blank forms.
I recommend to implement each form independently of the others (atomically) and handle the state passing on a different application layer. The reason is that each form will in practice handle data validation differently, write its outputs differently, handle errors differently, and we will probably want to extend or change it over time, so we don't need to know the context of the whole process and change dozens of sites to do so.
When processing the first form, we want to first validate the data received and if it is correct, then redirect the user to the second step. It is a good idea to handle the redirect as an HTTP redirect, because it can easily happen that the data is not valid, in which case we want to return the user to the first form and not to the next step.
We can basically store states in 4 ways:
None of these solutions is perfect or the only correct one. I myself combine multiple approaches when working on multi-step solutions. Typically, for example, I solve a shopping cart as a database table, to which I assign the data I have already collected and bind it either to a user (if he is logged in) or to a session (if he is not logged in and we don't know each other yet).
Jan Barášek Více o autorovi
Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.
Rád vám pomůžu: