Criação de Validators com Zend_Form

O ZendFramework possui muitos validators prontos, porém algumas vezes é necessário criar alguns específicos, como por exemplo a validação de CPF, que por padrão não tem no Framework. Mas e como fazer isso? Descreverei a seguir:

Primeiro vamos criar o Validate no caminho lybrary/My/Validate/Cpf.php

<?php
/**
 * @see Zend_Validate_Abstract
 */
require_once 'Zend/Validate/Abstract.php';

class My_Validate_Cpf extends Zend_Validate_Abstract {

	const NOT_CPF = 'notCpf';

	protected $_messageTemplates = array(
        self::NOT_CPF        => "'%value%' não é um CPF válido.",
    );

	protected $_numericOnly;
	/**
	 * Se o CPF for apenas numerico 12345678909 então $numbersOnly é true
	 * Se não, 123.456.789-09
	 * @param bool $numbersOnly
	 */
	public function __construct($numbersOnly = false)
	{
		$this->_setNumericOnly($numbersOnly);
	}

    /**
     * Sets $_numericOnly
     * @param bool $bool
     */
    private function _setNumericOnly($bool = false){
        $this->_numericOnly = $bool;
        if($this->_numericOnly === true){
            $this->_regexp = '/^(d){11}$/';
        }
        else{
            $this->_regexp = '/^(d){3}(.d{3}){2}-(d){2}$/';
        }
        return $this;
    }

	/**
	 * 
	 * @param   mixed $value 
	 * @return  boolean 
	 * @throws  Zend_Valid_Exception If validation of $value is impossible      
	 * @see Zend_Validate_Interface::isValid()
	 */
	public function isValid($value) {
		// checks regexp, first and second validation Digit
         if ( preg_match($this->_regexp, $value)
             && $this->_checkDigitOne($this->_removeNonDigits($value))
             && $this->_checkDigitTwo($this->_removeNonDigits($value))
                                                                                ){
            return true;
         }
         $this->_setValue($value);
         $this->_error(self::NOT_CPF);
         return false;
	}

    /**
     *
     * @param string $value
     * @return bool
     */
    private function _checkDigitOne($value)
    {
        $multipliers = array(10,9,8,7,6,5,4,3,2);
        return $this->_getDigit($value, $multipliers) == $value{9};
    }

    /**
     *
     * @param string $value
     * @return bool
     */

    private function _checkDigitTwo($value)
    {
        $multipliers = array(11,10,9,8,7,6,5,4,3,2);
        return $this->_getDigit($value, $multipliers) == $value{10};
    }

    /**
     *
     * @param string $value
     * @param array(int) $multipliers
     * @return int
     */
    private function _getDigit($value, $multipliers)
    {
        foreach($multipliers as $key => $v){
            $sum += $value{$key} * $v;
        }
        $digit = $sum % 11;
        if ($digit < 2) {
            $digit = 0;
        }else{
            $digit = 11 - $digit;
        }
        return $digit;
    }

    /**
     *
     * @param string $value
     * @return string
     */
    private function _removeNonDigits($value)
    {
        return preg_replace('/D/', '', $value);
    }
}
?>

O validate já está criado e a forma de usa-lo é bem semelhante aos demais validators, vamos então criar o formulário no caminho: application/forms/ValidaCpf.php.

<?php
class Application_Form_ValidaCpf extends Zend_Form {
	public function init()
    {
    	Zend_Dojo::enableForm($this);
    }

	public function __construct($options = null) {
		parent::__construct ($options);
		//definicoes do campo CPF
		//$cpf = new Zend_Form_Element_Text('cpf');
		$cpf = new Zend_Dojo_Form_Element_TextBox('cpf');
		$cpf->setLabel('CPF: ')
			->setTrim(true)
			->setRequired(true)
			->addFilter('Alnum')
			->addFilter('StripTags')
			->addFilter('StringTrim')
			->addValidator('NotEmpty', true, array('messages' => array('isEmpty' => 'Preencha este campo antes de prosseguir!')))
			->addPrefixPath('My_Validate', 'My/Validate/', 'validate')
			->addValidator('Cpf', true, array(true));
		//definicoes do botao de enviar
		$enviar = new Zend_Dojo_Form_Element_SubmitButton('enviar');
		$enviar->setLabel('Enviar');

		$this->addElements(array($cpf, $enviar));
	}
}
?>

Instancie o Form onde ele será utilizado e pronto, no caso aqui para exemplo será utilizado no IndexController.php na action validaCpf no caminho: application/controllers/IndexController.php

<?php 
class IndexController extends Zend_Controller_Action
{

    public function init()
    {
        Zend_Dojo::enableView($this->view);
    }

    public function indexAction()
    {
        // action body
    }

    public function validaCpfAction() {
    	$form = new Application_Form_ValidaCpf(array(
        		'action' => $this->view->baseUrl().'/index/valida-cpf',
        		'method' => 'post'));

    	$this->view->form = $form;
    	//se o formulario foi enviado via post
    	if ($this->getRequest()->isPost()) {
    		//recupera os dados enviados
            $formData = $this->getRequest()->getPost();
            //se o formulario foi preenchido corretamente
            if ($form->isValid($formData)) {
            	echo 'valido';
            }else {
            	//popula os campos com os dados preenchidos
                $form->populate($formData);
            }
    	}
    }
?>

e por fim a view no caminho: application/views/scripts/index/valida-cpf.phtml

Validação de CPF
<?php 
echo $this->form;