symfony Workshop – How to handle a form from a component in an action
In yesterdays post I boldly proclaimed saving symfony forms in dedicated actions rather than in components as a best practice.
A comment by Francisco then pointed out that this way the validation errors go missing when redisplaying the form.
So I had to get my hands dirty and show some code.
First of all lets see what we want to achieve.
- There must be a component displaying a form that can be included in any template.
- After submitting and handling the form the original page must be loaded again.
- The form input must be handled (validation and saving) in an action to avoid processing not handling related code.
- The data input by the user must be saved if validated.
- The form must show the errors if the validation failed.
- After successful saving a page reload must not trigger another saving attempt.
I think that’s all. So lets see how to achieve that.
I start with a very simple schema definition in order to have a form generated with a validation that can easily be failed during testing.
Next lets have a look at the component that renders the form.
In this component you see that first we create a form instance and then set a variable with the current URL unless it is set on the request already (you will see in a moment what this is for).
In case the current request is a
POST request we bind the form values from the current request to the form instance (line 11-14). This is necessary for the form instance to know about any validation errors.
The result of this component is the following partial template.
There is not much to explain on this template except that its action points to the route that calls the saving action and that we manually add a
redirect_to to it containing the URL of the originating page (This is quite useful to keep form data and redirect URL separate).
When the user submits this form it is sent to the following action.
The code on the lines 9-18 should be fairly familiar to you. The form instance is created, values are bound to it and if it is valid the form will be saved (I could have checked for the current request being a
POST request but I’m assuming this is added as a requirement to the route definition).
When the form is successfully saved a redirect to the passes redirect_to URL is issued loosing all
POST data and sending the originating page to the users browser.
If the form did not validate a redirect would not help as then all validation errors would be lost (see Franciscos comment). So instead we want to do a forward keeping the current
The forward is a bit more tricky than a redirect as it does not accept a URL but only a module/action pair.
So in line 20 we strip any scriptname (i.e.
/frontend_dev.php) from the
redirect_to URL. In line 21 we get a route definition matching this URL and in line 23 we do the forward.
Update: And on line 22 we add the originating pages route parameters to the current request so that the page has all information it needs and previously had.
In my experiment I used nothing but the default routes (
/:module/:action) and still the forward went to the correct module/action. So no worries.
And that’s it. All the above requirements are met. Now you can include the above form component in any template you like and it will (fingers crossed) just work.