The validation file satisfies most needs, but when the validation is very complex, it might not be sufficient. In this case, you can still return to the validateXXX()
method in the action, or find the solution to your problem in the following sections.
10.4.1. Creating a Custom Validator
Each validator is a class that extends the sfValidator
class. If the validator classes shipped with symfony are not suitable for your needs, you can easily create a new one, in any of the lib/
directories where it can be autoloaded. The syntax is quite simple: The execute()
method of the validator is called when the validator is executed. You can also define default settings in the initialize()
method.
The execute()
method receives the value to validate as the first parameter and the error message to throw as the second parameter. Both are passed as references, so you can modify the error message from within the method.
The initialize()
method receives the context singleton and the array of parameters from the YAML file. It must first call the initialize()
method of its parent sfValidator
class, and then set the default values.
Every validator has a parameter holder accessible by $this->getParameterHolder()
.
For instance, if you want to build an sfSpamValidator
to check if a string is not spam, add the code shown in Listing 10-32 to an sfSpamValidator.class.php
file. It checks if the $value
contains more than max_url
times the string 'http'
.
Listing 10-32 - Creating a Custom Validator, in lib/sfSpamValidator.class.php
class sfSpamValidator extends sfValidator
{
public function execute(&$value, &$error)
{
// For max_url=2, the regexp is /http.*http/is
$re = '/'.implode('.*', array_fill(0, $this->getParameter('max_url') + 1, 'http')).'/is';
if (preg_match($re, $value))
{
$error = $this->getParameter('spam_error');
return false;
}
return true;
}
public function initialize ($context, $parameters = null)
{
// Initialize parent
parent::initialize($context);
// Set default parameters value
$this->setParameter('max_url', 2);
$this->setParameter('spam_error', 'This is spam');
// Set parameters
$this->getParameterHolder()->add($parameters);
return true;
}
}
As soon as the validator is added to an autoloadable directory (and the cache cleared), you can use it in your validation files, as shown in Listing 10-33.
Listing 10-33 - Using a Custom Validator, in validate/send.yml
fields:
message:
required:
msg: The message field cannot be left blank
sfSpamValidator:
max_url: 3
spam_error: "Leave this site immediately, you filthy spammer!"
10.4.2. Using Array Syntax for Form Fields
PHP allows you to use an array syntax for the form fields. When writing your own forms, or when using the ones generated by the Propel administration (see Chapter 14), you may end up with HTML code that looks like Listing 10-34.
Listing 10-34 - Form with Array Syntax
<label for="story_title">Title:</label>
<input type="text" name="story[title]" id="story_title" value="default value"
size="45" />
Using the input name as is (with brackets) in a validation file will throw a parsed-induced error. The solution here is to replace square brackets []
with curly brackets {}
in the fields
section, as shown in Listing 10-35, and symfony will take care of the conversion of the names sent to the validators afterwards.
Listing 10-35 - Validation File for a Form with Array Syntax
fields:
story{title}:
required: Yes
10.4.3. Executing a Validator on an Empty Field
You may need to execute a validator on a field that is not required, on an empty value. For instance, this happens with a form where the user can change their password (but may not want to), and in this case, a confirmation password must be entered. See the example in Listing 10-36.
Listing 10-36 - Sample Validation File for a Form with Two Password Fields
fields:
password1:
password2:
sfCompareValidator:
check: password1
compare_error: The two passwords do not match
The validation process executes as follows:
- If
password1
== null
andpassword2 == null
:- The
required
test passes. - Validators are not run.
- The form is valid.
- The
- If
password2 == null
whilepassword1
is notnull
:- The
required
test passes. - Validators are not run.
- The form is valid.
- The
You may want to execute your password2
validator if password1
is not null
. Fortunately, the symfony validators handle this case, thanks to the group
parameter. When a field is in a group, its validator will execute if it is not empty and if one of the fields of the same group is not empty.
So, if you change the configuration to that shown in Listing 10-37, the validation process behaves correctly.
Listing 10-37 - Sample Validation File for a Form with Two Password Fields and a Group
fields:
password1:
group: password_group
password2:
group: password_group
sfCompareValidator:
check: password1
compare_error: The two passwords do not match
The validation process now executes as follows:
- If
password1 == null
andpassword2 == null
:- The
required
test passes. - Validators are not run.
- The form is valid.
- The
- If
password1 == null
andpassword2 == 'foo'
:- The
required
test passes. password2
isnot null
, so its validator is executed, and it fails.- An error message is thrown for
password2
.
- The
- If
password1 == 'foo'
andpassword2 == null
:- The
required
test passes. password1
isnot null
, so the validator forpassword2
, which is in the same group, is executed, and it fails.- An error message is thrown for
password2
.
- The
- If
password1 == 'foo'
andpassword2 == 'foo'
:- The
required
test passes. password2
isnot null
, so its validator is executed, and it passes.- The form is valid.
- The