7 Days of Symfony 1.1 – Forms, Widgets and Validators (Day2)
Effortlessly generating your forms
posted on Dec 15th
Note: This article is up to date through revision 7136. No promises after that - but I will try to stay up to date.
Day 2, time to actually makes some forms. Let's jump right in. First, let's define a model to play with:
That's it - just a simple model so that can exercise the new handling of forms in symfony. The command to build the model in Symfony 1.1 is exactly the same, but it packs a new, very powerful, punch. From the command line:
As usualy, Symfony generates helping models in your lib/model project directory. However, Symfony now adds a NEW directory - lib/forms. Just like with the model classes in lib/model, Symfony has automatically generated form classes to make creating and handle forms a breeze.
Let's go ahead and immediately see this in action. Create a new module, I'll call mine "articles". In that model, let's create an action called editAuthor:
Now, in the template editAuthorSuccess.php:
Et voila! Navigate of to this action to find a completed and formatted form. Most fields are textboxes, as they properly should be. You'll also notice that the birthday field has been output as a select year, month date field. As we'll find out later, foreign keys will automatically show up as select boxes as well.
Go ahead and submit the form. Nothing happens - it just submits back to the same page and reprints the form. Back in your actions, add this:
Now, refresh tha page and submit. What has changed. Well actually, ALOT. Try putting your name into the name fields, when you hit submit, they'll come back with your name automatically in them. But there's even more. Try putting a string like "ten" into the "years experience" field. When you submit now, you should get an error! That's right, you haven't actually written any form code, but your form is already validating. Remember how our name fields were of type varchar(30)? Try putting more than 30 characters into either of those fields. Once again, Symfony automatically validates the field and throws a validation error. What's more, we're just one line of code away from saving the author to the db if - and only if - validation clears.
How did Symfony go about setting up that form for you? Let's look at the file lib/forms/AuthorForm.class.php:
Just like with the models, Symfony automatically gives you a file that you can modify (which we'll do a bunch of later). The true work is done in the base class BaseAuthorForm located in the base subfolder (again, just like the models). Let's look at that file:
There's a bit more to this file that I left out, but we'll look into it with more detail coming up. The code here may be a little daunting, but let's sort it out.
Every form is made up of widgets. What's a widget? A widget is anything that produces html. More specifically, each widget represents each form field of our form. When we echo the form, it simply spits out all of the widgets inside that form. It's that simple, it really is.
To help "hold" the widgets that are part of a form, we have a sfWidgetFormSchema class. Now, that's much more complicated than it sounds like. In essence, all that class does is act as an array that holds the widgets of the form. It really is that simple. By calling the setWidgets method, we basically put all of the widgets (in this case there's 4 of them) inside our sfWidgetFormSchema class (which acts just like an array!). The setWidgets method handles interaction with sfWidgetFormSchema so we don't have to. But, it's honestly very simple - this basically just allows us to write less.
When a form is created (in our case, via $this->form = new AuthorForm), the setup() method above is ran. It's first job is to give this empty form some real-life fields. The setWidgets does this job. Remember that the sfWidgets just puts our widgets into our array-like object, sfWidgetFormSchema.
That's the beauty of it. Symfony is setting up your form to automatically have all the necessary fields AND have them use the correct form element. There are more form elements than the above, and we'll get to a lot of them in the coming days.
Just as forms contain widgets, forms also contain validators with the same name as their associated widgets. Once again, we have an sfValidatorSchema - fancy word for the array that holds the validators of our form. In this case, the method setValidators takes care of populating our sfValidatorSchema object.
Now that we've got all our necessary fields, we want to setup some validation on those fields. This is just as simple:
We're going to look into MANY more things that validators can do in the coming days. One last piece of important code that handles the format of the "name" attribute in the outputted html
Let's quickly revisit our action before the day is up look it over again.
Now this makes more sense. When we create the form, it automatically sets up all the basic fields and validators. What about the bind() method? The bind method is quite powerful. It inputs the data that was submitted (store in $this->getRequestParameter('author') in this case) and "binds" it with the form object. This has many effects:
- it validates the data and throws errors where necessary
- it (in the upcoming days) allows you to "save" the form, which will save the new data to the model object
- if it has to print the form out again, it will do so with the new "binded" data.
That's it for today - what a marathon. Tomorrow we'll greatly enhance our project by adding all sorts of validation, modifying our widgets and actually saving our new authors!