12.17.07

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

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).

Welcome to day 4 of our 7 day series on the new Symfony 1.1 forms, widgets and validation system. Yesterday we used our form to save our object to the database. Today we're going to tidy up that form by adding validation.

First, let's make our first name and last name fields required. In our AuthorForm class, notice the new additions near the bottom:

public function configure()
  {
  	$widget_schema = $this->getWidgetSchema();
  	$birthday_widget = $widget_schema['birthday'];
  	
  	$years = range(date('Y') - 75, date('Y') - 10);
  	$birthday_widget->setOption('years', array_combine($years, $years));
  	
  	$widget_schema['years_experience']->setAttribute('size', 3);
  	
  	$this->setDefault('years_experience', 0);
  	
  	$this->validatorSchema['first_name']->setOption('required', true);
  	$this->validatorSchema['last_name']->setOption('required', true);
  }

Try submitting your form without the first_name or last_name field. You'll now see a validation error with the description "required". Notice how similar the widgetSchema and validatorSchema are. Each are simply array-like objects that hold widgets and forms for the respective form. Just like with the widgets, we access our validators simply by calling their name.

// Access the validator responsible for our 'first_name' field. Set it's "required" option to true
$this->validatorSchema['first_name']->setOption('required', true);
// Access the validator responsible for our 'last_name' field. Set it's "required" option to true
$this->validatorSchema['last_name']->setOption('required', true);

You should also notice another similarity between widgets and validators. That is, each is controlled by "options", which ultimately determine how each behaves. Just like with widgets, each validator can accept different options that control how the data is validated. If you look in your BaseAuthorForm, you'll see that the 'first_name' and 'last_name' fields are using the sfValidatorString object, which allows you to do basic string validation. Let's try out another option available to that particular validator.

public function configure()
  {
  ...
  $this->validatorSchema['first_name']->setOption('min_length', 3);
  $this->validatorSchema['last_name']->setOption('min_length', 3);
  }

And with that code, each field is now required AND has a minimum length of 3 characters. As a side note, if you have a min_length option on a field with the required option set to false, the user WILL be able to submit the form empty (with 0 characters), even though it's technically less than your min_length option.

Let's move on and add email validation to our email field. If you look back to our BaseAuthorForm class, you'll see that this field also uses the sfValidatorString object. This validator is used by default for all string fields as defined in the schema.yml file. Since we want to validate this field to be sure it is in a valid email format, let's change our validator from sfValidatorString to sfValidatorEmail.

public function configure()
  {
  ...
  $this->validatorSchema['email'] = new sfValidatorEmail(array('required' => true));
  }

Let's walk through this. First, our email field validator is currently accessed via $this->validatorSchema['email']. It currently uses the sfValidatorString validator, but we'd rather replace that with the sfValidatorEmail. Each field has exactly 1 validator (I'll show you later how to use multiple validators), so by re-setting the validator like above, we make our email field now use the sfValidatorEmail validator. This validator doesn't require any options, but I've included the required options so that the field not only needs to be of email format, but also present.

When you try it out, you'll see the "Required" message if you leave it blank, and an "Invalid" message if you put something that's not an email. The "Invalid" message is a little archaic, so let's change that to something a little more helpful.

public function configure()
  {
  ...
  $this->validatorSchema['email'] = new sfValidatorEmail(array('required' => true));
  $this->validatorSchema['email']->setMessage('invalid', 'Please enter a valid email address');
  }

Every validator error has an internal name and an associated message. In the case of the email validator, the internal name is 'invalid' with a default message of "Invalid". In fact, this is the default "validation has failed" error name and message. By calling the setMessage() method, we can override this method and print out a slightly more helpful message.

public function configure()
  {
  	...
  	$this->validatorSchema['website'] = new sfValidatorUrl(array('required'=>false));
		$this->validatorSchema['website']->setMessage('invalid', 'Please enter a valid web address (http://google.com)');

You can probably already tell what we've done here. This time, I passed the required option as false so that we don't require any input in this field. Actually, if you don't pass a required option, it is always assumed true. Thus, passing the required=true parameter to our email validator was redundant. Now, if you try to submit with an invalid url, you'll get the nice neat error message you expect.

Let's do one more and then take a break for the day. Let's say that we require authors to have at least 5 years experience to be added. You'll see from the BaseAuthorForm class that the years_experience field already uses the sfValidatorInteger validator. We simply want to add a minimum to the input.

public function configure()
  {
	  ...
		$validator_schema['years_experience']->setOption('min', 5);
		$validator_schema['years_experience']->setMessage('min', 'Authors must have at least 5 years experience');

Just one quick thing to note. In this case, the error name wasn't invalid, but rather 'min', which is what we used to reformat our message.

There are a lot more validators that we'll see in coming days as we develop our application. Coming up, we'll look into two relationship fields and how they work together. Like normal, Symfony will give us a big head start by guessing the proper formatting of our form from our model. See you then.

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