12.21.07

7 Days of Symfony 1.1 - Forms, Widgets and Validators (Day6)

Posted by ryan in symfony


Note: Many things here may be out of date. This article is up to date only through early 2008 (which was a while ago).

We've covered a lot of the new widgets and validators while creating our now somewhat mature application (at least as far as our forms and validation). For the most part, once you get the hang of using widgets and validators, the process becomes repetitious and straightforward. The only trick is finding out exactly what other widgets and validators are available. Today we're going to run through nearly all the widgets and validators so that you'll be ready to use them with your application.

The Widgets

After defining the two base widgets, only the widgets with anything interesting or tricky about them are listed. If it's not listed, it's probably pretty straightforward (or similar to something else).

sfWidget

This is the base widget for all widgets. While you'll never actually use it (it's an abstract type, it does have a few methods that you'll want to use from time to time. A lot of these will be helpful when you graduate beyond the simple use of widgets to more complex types. Also, as more widgets are created for Symfony (not just form widgets), they'll also have all of these features.

getAttribute($name) and getAttributes() - Getter method for either an individual attribute or all the attributes. Remember, attributes are printed out inside the tag in the following way: array('id'=>'my_element') will yield the id="my_element" attribute inside your tag
getOption($name) and getOptions() - Getter method for either an individual option of all the options. Remember that the options are specific to the widget and control its behavior. Common options include 'is_hidden', 'days' (used by the datetime widget), and 'multiple' which is determines if a form select is 1 element high or more.
renderTag($tag, $attributes) - generates html of the form . Used, obviously, for creating tags like the input tag.
renderContentTag($tag, $content, $attributes) - generates html of the form content
Also remember that there are setter methods for attributes and options (obviously).

sfWidgetForm

There's also a sfWidgetForm class which offers a few options across all the form widgets.
Options:
id_format=%s - if no id is manually set, the id field will automatically be set using the name of the form with the formatting of id_format. You'll notice that our generated forms change the id_format to something like article[%s]
is_hidden=false - if set to true, the field will print out as a hidden field with whatever current default value.
needs_multipart=false - I don't know of any functionality this has, but it allows you to call the needsMultipartForm() method to determine if you need to include a multipart enctype declaration in your form.

sfWidgetFormInputPassword

Options:
always_render_empty=true - makes sure that the field never tries to populate a value after binding the form with data or a model

sfWidgetFormSelect

Options
choices - the array of options to use for this select tag. It should be formatted in normal key=>val format. This can also optionally be a function that returns the particular array if used in this fashion: 'choices' => new sfCallable(array($this, 'getAuthorIdentifierChoices')), which is exactly stolen from the generated form from our project.
multiple=false - this determines whether or not the select tag should be set up to allow for multiple selections. The sfWidgetFormSelectMany widget is identical to this widget, except that it sets this option to true by default.

sfWidgetFormSelectRadio

options
choices - see the sfWidgetFormSelect explanation, choices are used in the same way.
label_separator= - the string that is placed between the radio tag and its label
separator=" " - the string the separates each individual radio element
formatter=array($this, 'formatter') - location of the function that should be called to finally put the whole element together. This is a little more advanced. There is a default formatter $this->formatter($inputs) that's called. This function returns the full, decorated tag. This is akin to decorators, which will study more tomorrow.

sfWidgetFormTextarea

This object simply has 2 default attributes:
rows=4
cols=30

Other Widgets not mentioned

  • sfWidgetFormInputCheckbox
  • sfWidgetFormInputFile
  • sfWidgetFormInputHidden
  • sfWidgetFormInput
  • sfWidgetFormDate
  • sfWidgetFormTime
  • sfWidgetFormDateTime
  • sfWidgetFormSelectMany

The Validators

Just like with the widgets, I'll just mention some details about some of the more complicated validators. It it didn't make the list, it's probably straightforward of very similar to another validator.

sfValidatorAnd

the sfValidatorAnd validator is a special type of validator that allows you to combine different validators for one end result. For example, imagine if you wanted to validate a string as an email address AND make sure that the email address has a length longer than three. To do this, you would need to use both the sfValidatorString and the sfValidatorEmail. The code would look something like this:

$v = new sfValidatorAnd(array(
	new sfValidatorString(array('men_length'=>3)),
	new sfValidatorEmail()
));
$this->validatorSchema['email'] = $v;

The sfValidatorAnd method will fail if ANY of the associated validators fail. If the string is less than 3 characters long OR is not of the form of an email, then the validator will not validate. In addition to passing the validators you want to use as the first argument in the constructor, you can also add validators via sfValidatorAnd method addValidator.

sfValidatorOr

This works exactly like sfValidatorOr except that it only fails if ALL of the associated validators fail. A better way of saying this is that it will successfully pass a value of any of its associated validators pass. Let's say now that we have a field that can either be an email OR a website url.

$v = new sfValidatorOr(array(
	new sfValidatorUrl(),
	new sfValidatorEmail()
));
$this->validatorSchema['email_or_website'] = $v;

Now the 'email_or_website' field will now validate if it is an email OR a website. Let's take this a step further. Suppose that we have the same field - one that can be an email OR a website - but we want to require that field to have at least 3 characters.

3)) )); $this->validatorSchema['email_or_website'] = $v_all;

As you can see, after creating the $v_any validator - which requires the field to be either an email or a website - we combine it with the string validator inside an sfValidatorAnd. The result is this. Because the main validator is the sfValidatorAnd validator, both the $v_any validator and the sfValidatorString validator must pass. The latter simply requires the string to have 3 characters. The former requires the string to either be an email or a website. By combining the validators in this way, we've now created a new validator that accepts emails or websites, but requires at least 3 characters.

sfValidatorBoolean

This validator checks the input value to see if it represents either a "true" or a "false" value. True values are defined by any of the 'true_values' option, which is defined in the following way:
array('true', 't', 'yes', 'y', 'on', '1')
The false values are defined by any of the 'false_values' defined by:
array('false', 'f', 'no', 'n', 'off', '0')
The validator works in this way. If the input value matches any of the true values, it will return a clean value of true (the actual boolean type value true). The same goes for false - if the input value matches any of the false values, the validator returns the clean value (boolean)false. How does the validator fail? If the input value doesn't match any true or false values, validation will fail with an error of 'invalid'. So, this validator not only translates user input to a legitimate boolean true / false value, but also makes sure that the input value is a valid true or false value. Since both the 'true_values' and 'false_values' arrays are options, you can modify them using the usual setOption() syntax on the validator.

sfValidatorCallback

This, the true custom validator, sends the validation duties to an external function to handle validation. The validator requires the option 'callback', which defines what function to call to handle validation. The function is eventually called in the following way:
call_user_func($this->getOption('callback'), $this, $value);
You can see that not only is the function called, but both the validator itself AND the input value are passed to your function. The job of your function will be that of any normal validator and is two-fold:
1) Determine if the input value is valid. If it is not valid, throw a sfValidatorError error.
2) Clean the input value and output the cleaned output value. For example, the sfValidatorBoolean validator takes in a string version of a boolean, and returns (as the clean value) a legitimate true / false of boolean type.

sfValidatorChoice

This validator is simple, and beautiful. It requires the option 'choices', which represent an array of valid values that this validator should accept. For example, imagine you want to validator a text field for the name of a day of the week. Then, use might do the following:

$v = new sfValidatorChoice(array(
						'choices'=>array('Monday, 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')
));

Validation will now pass if the input value is one of the the above values. You must be careful, however, when using this like we are above as the validator is case-sensitive. The items in the 'choices' array may also be instances of the sfCallable class, whose return values will be used as the item to match against.

sfValidatorCSRFToken

Shame on me for not mentioning this validator yet! But, good job to Symfony for hiding its functionality so thoroughly. Let me explain. There is a new feature that comes standard with the new form system. This new feature is inserted into your code and handled completely silently (as it should be). That feature is the inclusion of a CSRF token in every form. These tokens protect against cross-site form attacks (wiki CSRF), and basically make your form much more secure. The beautiful thing is that you don't have to handle the code anywhere (unless of course you want to). the token is automatically added as a hidden field in your form, and its value is automatically validated using this validator. If you ever get an sfValidatorCSRFToken error when you weren't doing anything abnormal, chances are that you somehow accidentally cleared the CSRF token field. I've done this several times before - most commonly by calling the setDefaults() method instead of the setDefault method, whereby I accidentally cleared the correct default CSRF value.

sfValidatorDate

The job of this validator is simple - determine if the input is a valid date, then output in a cleaned format. The input value can be of a number of different formats including a mktime-style array, unix timestamp, strtotime format, or a custom format that you can define. The following messages and options are standard:

    // Defines the error message to get if the input value doesn't match the 'date_format'
    $this->addMessage('bad_format', '"%value%" does not match the date format (%date_format%).');
		
		// Optional date_format can be used if your input value is going to be of some special format
    $this->addOption('date_format', null);
    // If time is to be used with this date. Setting this to true is exactly what the sfValidatorDateTime validator does
    $this->addOption('with_time', false);
    // defines the output of the date if with_time is false. By default, it uses standard mySql format
    $this->addOption('date_output', 'Y-m-d');
    // defines the output of the data if with_time is true
    $this->addOption('datetime_output', 'Y-m-d H:i:s');
    // optional error message to be sent along when the input format fails our 'date_format' format
    $this->addOption('date_format_error');

sfValidatorInteger

This is quite straightforward. It fails if the input isn't an integer, and allows you to specify max and min values. It has the following default options:

    $this->addMessage('max', '"%value%" must be less than %max%.');
    $this->addMessage('min', '"%value%" must be greater than %min%.');

    $this->addOption('min');
    $this->addOption('max');

    $this->setMessage('invalid', '"%value%" is not an integer.');

sfValidatorNumber

This is exactly the same as the above sfValidatorInteger except that it requires any number - not just any integer.

sfValidatorPass

This is type of dummy validator. It always validates successfully and returns the raw input value as the clean value.

sfValidatorRegex

This is a handy validator that requires the 'pattern' option. The input value is then matched to the 'pattern' using preg_match. If the return value of the preg_match is false, validation fails.

That's it! I've left out several validators for a number of reasons. The sfValidatorChoiceMany is exactly like the sfValidatorChoice, except that instead of comparin one value to see if it is an array, if compares an array of values to see if each is in an array. Also, the sfValidatorDateTime is equivalent to using the sfValidatorDate validator with the with_time option set to true.

Additionally, there are several schema validators which are a bit more advanced, and a decorator validator as well. We'll be looking at the decorators tomorrow, and hopefully at the schema more in the future. See you then.

Thanks for the shares!
  • StumbleUpon
  • Sphinn
  • del.icio.us
  • Facebook
  • TwitThis
  • Google
  • Reddit
  • Digg
  • MisterWong