This forum is in READ-ONLY mode.
You can look around, but if you want to ask a new question, please use the new forum.
Home » development » Documentation » [tutorial] How to create a custom validator in 1.1  (5) 3 Vote(s)
[tutorial] How to create a custom validator in 1.1 [message #62914] Sun, 12 October 2008 13:21 Go to next message
pascal.duverge  is currently offline pascal.duverge
Messages: 20
Registered: August 2007
Location: Brussels, Belgium
Junior Member
Hello all,

I had to make a custom validator in 1.1 and found no docs for that.

Here is how I did, I hope it could be a draft for the form book.

Example context

Let's say we work on a community website. Users can upload a picture for their profile.

A basic validation for file works fine :
  • the max size in KBytes is checked
  • the image mime type is checked too


But what about the picture size in pixels?
It could be fine to have pictures with size between 100 & 250 px width or height.

Let's make a custom validator.

File organization and inheritance

A validator is a class, so we'll put it in a /lib directory.

Note :
we'll put the custom validators in the /apps/lib directory, in order to make it available everywhere in the project.
If it is a big project that needs structuration, we can create and use the subdirectory /apps/lib/validator

The file name follow the standards of sf, so for a myCustomValidator class, the file is myCustomValidator.class.php . (be carefull for UPPER/lower case)

Each validator extends the sfValidatorBase or any validator extending sfValidatorBase.

For image validation, we'll validate the file and the picture size. so, the class declaration looks like :

// the image validator extending the file validator
class sfValidatorFileImage extends sfValidatorFile
{
}

// a basic custom validator
class myCustomValidator extends sfValidatorBase
{
}


Creation of validator options and error messages

As we have seen in the form book ch3, the constructor of a validator needs 2 arrays to work:
  1. a first array of rules, called "options"
  2. a second array of error messages, called "messages"


We define these arrays in the configure() method. To add an option or a message, simply use addOption() and addMessage().
If we extend a concrete validator, we have to call parent::configure() method.

protected function configure($options = array(), $messages = array())
  {
    /* size image options */
    $this->addOption('min_size_x');
    $this->addOption('max_size_x');
    $this->addOption('min_size_y');
    $this->addOption('max_size_y');
    /* we set a default value as image for mime type */
    $this->addOption('mime_types' , 'web_images');


    /* size image error messages */
    $this->addMessage('min_size_x', 'Image is not large enough (minimum is %min_size_x% pixels).');
    $this->addMessage('max_size_x', 'Image is too large (maximum is %max_size_x% pixels).');
    $this->addMessage('min_size_y', 'Image is not tall enough (minimum is %min_size_y% pixels).');
    $this->addMessage('max_size_y', 'Image is too tall (maximum is %max_size_y% pixels).');
    /* mime type error message */ 
    $this->addMessage('mime_types', 'Invalid mime type (%mime_type%). It is not an image file.');
   
    /* let's use the parent configure() to keep file validator configure() */    
    parent::configure($options, $messages);
  }


syntax :
addOption('option_name') defines an option.

To have a default value, simply use addOption('option_name', 'default_value)

To define error message, use:
$this->addMessage('option_name', 'Error message for human being');

To use a value in the error message called "size_x" for example,
simply write it %size_x%. It will be replaced with the value in the error message.

Nice Smile We defined how to use the validator, but not how it validates data. Let's do it.

Heart of validation : doClean()

We did the very easy part of customization. Now, let's make the easy part.

We check 2 things for each option (rule):
  1. if the option was defined in the form validation
  2. if the rule for that option is OK


We'll not forget to use parent's doClean() return value.

For a maximum size (width - called "x"), the code looks like:
 protected function doClean($value)
  {
    $validatedFile = parent::doClean($value);
    // we get the image size
    $size = getimagesize($validatedFile->getTempName());
    
    // check "image size x" vs "max_size_x"
    if ($this->hasOption('max_size_x') && $this->getOption('max_size_x') < (int) $size[0])
    {
      throw new sfValidatorError($this, 'max_size_x', array('max_size_x' => $this->getOption('max_size_x'), 'size_x' => (int) $size[0]));
    }
    // other tests for options here
    // ...

    // return value
    return $validatedFile;
  }


$this->hasOption('option_name') checks the option was defined in the form validation class. If not, the rule is ignored.

If the rule is not fulfilled, an exception is raised :
throw new sfValidatorError($this, 'max_size_x', array('max_size_x' => $this->getOption('max_size_x'), 'size_x' => (int) $size[0]));

The syntax is :
throw new sfValidatorError($this, 'option_name', array('name' => 'value that will replace %name% in the error message' );

That's it Smile
We made a custom validator !

The full code for this Image validator:
// the image validator extending the file validator
class sfValidatorFileImage extends sfValidatorFile
{

protected function configure($options = array(), $messages = array())
  {
    /* size image options */
    $this->addOption('min_size_x');
    $this->addOption('max_size_x');
    $this->addOption('min_size_y');
    $this->addOption('max_size_y');
    /* we set a default value as image for mime type */
    $this->addOption('mime_types' , 'web_images');


    /* size image error messages */
    $this->addMessage('min_size_x', 'Image is not large enough (minimum is %min_size_x% pixels).');
    $this->addMessage('max_size_x', 'Image is too large (maximum is %max_size_x% pixels).');
    $this->addMessage('min_size_y', 'Image is not tall enough (minimum is %min_size_y% pixels).');
    $this->addMessage('max_size_y', 'Image is too tall (maximum is %max_size_y% pixels).');
    /* mime type error message */ 
    $this->addMessage('mime_types', 'Invalid mime type (%mime_type%). It is not an image file.');
   
    /* let's use the parent configure() to keep file validator configure() */    
    parent::configure($options, $messages);
  } // END configure()

 protected function doClean($value)
  {
    $validatedFile = parent::doClean($value);
    // we get the image size
    $size = getimagesize($validatedFile->getTempName());
    
    // check "image size x" vs "max_size_x"
    if ($this->hasOption('max_size_x') && $this->getOption('max_size_x') < (int) $size[0])
    {
      throw new sfValidatorError($this, 'max_size_x', array('max_size_x' => $this->getOption('max_size_x'), 'size_x' => (int) $size[0]));
    }
    // check size x vs min_size_x
    if ($this->hasOption('min_size_x') && $this->getOption('min_size_x') > (int) $size[0])
    {
      throw new sfValidatorError($this, 'min_size_x', array('min_size_x' => $this->getOption('min_size_x'), 'size_x' => (int) $size[0]));
    }

    // check size y vs max_size_y
    if ($this->hasOption('max_size_y') && $this->getOption('max_size_y') < (int) $size[1])
    {
      throw new sfValidatorError($this, 'max_size_y', array('max_size_y' => $this->getOption('max_size_y'), 'size_y' => (int) $size[1]));
    }
    // check size y vs min_size_y
    if ($this->hasOption('min_size_y') && $this->getOption('min_size_y') > (int) $size[1])
    {
      throw new sfValidatorError($this, 'min_size_y', array('min_size_y' => $this->getOption('min_size_y'), 'size_y' => (int) $size[1]));
    }

    // return value
    return $validatedFile;
  } // END doClean()

}


Note:
We do not check if min_size_x < max_size_x in the option definition, but we can manage it if we want.

Summary

So it is not complicated to create a custom validator for sf 1.1

Here is our check list for creating a validator:
  1. Check if a validator fulfill our needs, or a part of our needs
  2. Create a class extending the sfValidatorBase or an existing validator in the /app/lib directory
  3. Write the configure() method : define options (the named rules), messages (named error messages)
  4. Write the validation in doClean() : if an option is defined, check it and raise an error if rule is not fulfilled
  5. Test, test, test your validator


See you,

Pascal
icon14.gif  Re: [tutorial] How to create a custom validator in 1.1 [message #75679 is a reply to message #62914 ] Thu, 26 March 2009 17:12 Go to previous messageGo to next message
HiDDeN  is currently offline HiDDeN
Messages: 135
Registered: July 2006
Location: Barcelona, Spain
Senior Member
Excellent. This should be put into the cookbook.
Re: [tutorial] How to create a custom validator in 1.1 [message #80139 is a reply to message #75679 ] Thu, 18 June 2009 15:44 Go to previous message
pascal.duverge  is currently offline pascal.duverge
Messages: 20
Registered: August 2007
Location: Brussels, Belgium
Junior Member
Hi,

I translated this tutorial in french.

Il est donc disponible en fran├žais sur le site phpfrance :
http://www.phpfrance.com/forums/voir_sujet-248499.php

A+

Pascal
Previous Topic:link_to documentation confusion
Next Topic:Differences printed book and online PDF
Goto Forum:
  

powered by FUDforum - copyright ©2001-2004 FUD Forum Bulletin Board Software