Search code examples
phpzend-frameworkzend-formzend-dbzend-validate

How to use Zend_Validate twice


I'm using Zend_Validate_Db_NoRecordExists and I would like to validate my Zend_Form against two fields in DB, sample code:

    $phone_1 = new Zend_Form_Element_Text('phone_1');
    $phone_1->addValidator(new Zend_Validate_Db_NoRecordExists(
                            array(
                                'field' => 'phone_1',
                                'table' => 'customer',
                    )))
            ->addValidator(new Zend_Validate_Db_NoRecordExists(
                            array(
                                'field' => 'phone_2',
                                'table' => 'customer',
                    )));

Can I accomplish this?


Solution

  • Create validation chain

    $chain = new Zend_Validate();
    $foo   = new Zend_Validate_Db_NoRecordExists(...);
    $bar   = new Zend_Validate_Db_NoRecordExists(...);
    $chain->addValidator($foo)
          ->addValidator($bar);
    
    $element->addValidator($chain); 
    

    UPDATE @Bob Kruithof

    Quick and dirty sample using ZF 1.12.3. All code samples and line numbers are from ZF 1.12.3

    <?php
    
    $aPaths = array(
        'path/to/zf'
    );
    
    set_include_path(implode(PATH_SEPARATOR, $aPaths));
    
    require_once 'Zend/Loader/Autoloader.php';
    $autoloader = Zend_Loader_Autoloader::getInstance();
    
    $element = new Zend_Form_Element_Text('testSO');
    
    $chain = new Zend_Validate();
    $bar = new Zend_Validate_Regex('/^Test/');
    $baz = new Zend_Validate_Regex('/Valid$/');
    $chain->addValidator($bar)
          ->addValidator($baz);
    
    $element->addValidator($chain); 
    
    echo PHP_VERSION . ', ' . Zend_Version::getLatest() . PHP_EOL;
    
    var_dump($element->isValid('Test'));
    var_dump($element->isValid('Valid'));
    var_dump($element->isValid('Test Valid'));
    
    /* output
    5.3.8, 1.12.3
    bool(false)
    bool(false)
    bool(true)
    */
    
    ?>
    

    Why this works: Zend_Form_Element.php lines 1144 - 1168 sets validators either by Zend_Validate object or by string. You are right - if validators are used with name, not as chain, then as a result one would have single validator. But in result we have:

    $this->_validators[$name] = $validator;
    /* debugger info:
        $name --> (string) Zend_Validate
        $validator --> Zend_Validate object {
      _validators => array(2) (
        [0] => array(2) (
          [instance] => Zend_Validate_Regex object {
          }
          [breakChainOnFailure] => (bool) false
        )
        [1] => array(2) (
          [instance] => Zend_Validate_Regex object {
          }
          [breakChainOnFailure] => (bool) false
        )
      )
      _messages => array(0)
      _errors => array(0)
      zfBreakChainOnFailure => (bool) false
    }
    */
    
    public function addValidator($validator, $breakChainOnFailure = false, $options = array())
        {
            if ($validator instanceof Zend_Validate_Interface) {
                $name = get_class($validator);
    
                if (!isset($validator->zfBreakChainOnFailure)) {
                    $validator->zfBreakChainOnFailure = $breakChainOnFailure;
                }
            } elseif (is_string($validator)) {
                $name      = $validator;
                $validator = array(
                    'validator' => $validator,
                    'breakChainOnFailure' => $breakChainOnFailure,
                    'options'             => $options,
                );
            } else {
                require_once 'Zend/Form/Exception.php';
                throw new Zend_Form_Exception('Invalid validator provided to addValidator; must be string or Zend_Validate_Interface');
            }
    
    
            $this->_validators[$name] = $validator;
    
            return $this;
        }
    

    When we call $element->isValid()) it iterates over all validators inside of the chain - check Zend_Validate.php, lines 91-110:

    public function isValid($value)
        {
            $this->_messages = array();
            $this->_errors   = array();
            $result = true;
            foreach ($this->_validators as $element) {
                $validator = $element['instance'];
                if ($validator->isValid($value)) {
                    continue;
                }
                $result = false;
                $messages = $validator->getMessages();
                $this->_messages = array_merge($this->_messages, $messages);
                $this->_errors   = array_merge($this->_errors,   array_keys($messages));
                if ($element['breakChainOnFailure']) {
                    break;
                }
            }
            return $result;
        }
    

    $this->_validators is our array of validators:

    array(2) (
      [0] => array(2) (
        [instance] => Zend_Validate_Regex object {
          _messageTemplates => array(3) (
          )
          _messageVariables => array(1) (
          )
          _pattern => (string) /^Test/
          _value => null
          _messages => array(0)
          _obscureValue => (bool) false
          _errors => array(0)
          _translator => null
          _translatorDisabled => (bool) false
        }
        [breakChainOnFailure] => (bool) false
      )
      [1] => array(2) (
        [instance] => Zend_Validate_Regex object...