7 Days of Symfony 1.1 – Forms, Widgets and Validators (Day3)
Customize your widgets and validators - and finally save that object
posted on Dec 16th
Note: This article is up to date through revision 7136. No promises after that - but I will try to stay up to date.
Welcome to day 3 or the introduction to the new form system in Symfony 1.1. Yesterday we looked into the automatic generation of the Propel Form objects that fuel the forms for your objects. Today, let's clean up those forms and make them useful.
Yesterday, while we got to the point where we could submit our form (with validation), we didn't actually save our object to the database. For now, we'll make the form behave in the following way:
- If the forms submits and fails validation, the object is not saved and the form is returned with the data the user entered
- If the validation passes, then we'll save our object to the database and return a blank form so that we can enter in another author.
We've added a few things above. We remember from yesterday that we called the bind() method to connect the submitted data with the form object. Today, we call the new method isValid() which return true if validation passed, false otherwise. Once we know that our form is valid, we update and save the object.
Notice that if the form doesn't validate, we simply return the form as before - complete with validation errors and the user's data. Inside the template, add this to the top so that we print out the flash message:
Try it out, assuming you enter valid data - and our validation is pretty weak at this point - the form will save your author to the database, print out the success message and return a blank form.
What's next? Well, let's fix up our form a bit. For starters, the birthday field doesn't work right, as the years only extend in the 5 year radius of the current year. Let's change this up. For changes to our form that aren't going to be action-specific (we'll ALWAYS want our birthday field to have years that extend further back), we make our changes in the AuthorForm.class.php file of the lib/form directory. Remember, the configure() function is called when a form is created - so we can make our changes right in there.
Let's break it down. First, we're wanting to modify our birthday widget, so we need to know how to get at it. Fortunately, since we're in a subclass of sfForm, we can get at our widgetSchema quite easily via $this->widgetSchema. Remember, the widget schema is essentially the array that holds our widgets. So, to get the widget we want, we only have to do this:
Now that we know how to get to our widget, let's change the year range. Let's back up for a quick moment and explain widget options. The customization of a particular widget is done by setting certain "options" for that widget. Each widget has different options, and when the widget documentation is completed, you'll be able to easily see what options you can customize for each widget. In this case, the sfWidgetFormDate widget - which is responsible for our birthday widget - has several options:
To modify the years on our birthday widget, we just need to modify the 'year' option of the widget.
Try it out. We now have a birthday widget with more appropriate years.
What else should we change? Well, the years_experience field seems a little long. The field will be accepting values like "5" "15", and we'll want to shorten up this field a bit. Back to our configure() function - let's add the following after our existing code:
Things are getting easy now. First, we get at our years_experience widget by using its name as the array key of our $this->widgetSchema class variable. Then we use the setAttribute function to set size=3. If you didn't guess it already, the "attributes" part of a widget is all the stuff that's printed inside the html entity (e.g. class, style, onclick etc). For any widget, you can call the setAttribute function, and Symfony will automatically print it out with the form. In this example, we now have a size="3" portion of our form - making it an appropriate size for accepting years.
One more quick change to that years_experience field. Even though the field automatically validates to only accept a number (since we defined the field to be of type integer), let's make it more clear to the user that we're expecting a number and not a word like "ten". To do this, we'll set the default value of this field to "0".
That's it. Now, unless our form has a binded value for 'years_experience', the form will start with the value 0.
There's lots more we can do with forms. There's many widgets we haven't seen yet, and we haven't even started true validation of our form. Tomorrow we'll look into doing validation that will make our form truly useful. In the upcoming days, we'll see more form widgets, look into how relationships are handled, study the form decorator and more.