views:

366

answers:

1

I'm writing a custom validator that will validate against multiple other form element values. In my form, I call my custom validator like this:

$textFieldOne = new Zend_Form_Element_Text('textFieldOne');
$textFieldOne->setAllowEmpty(false)
             ->addValidator('OnlyOneHasValue', false, array(array('textFieldTwo', 'textFieldThree')));

My validator will check that only one of those three fields (textFieldOne, textFieldTwo, textFieldThree) has a value. I want to prevent a future developer from accidentally passing the same field twice.

$textFieldOne->addValidator('OnlyOneHasValue', false, array(array('textFieldOne', 'textFieldTwo', 'textFieldThree')));

So far, my validator works perfectly, except when I pass the same field name as the field that has the valiator set on it.

In my validator, you can see that I am checking that the value (of the element with the validator set on it). I'm also checking the values of the other fields that were passed to the validator.

public function isValid($value, $context = null) {

    $this->_setValue($value);
    $this->_context = $context;

    if ($this->valueIsNotEmpty()) {
     if ($this->numberOfFieldsWithAValue() == 0) {
      return true;
     }
     $this->_error(self::MULTIPLE_VALUES);
     return false;
    }

    if ($this->numberOfFieldsWithAValue() == 0) {
     $this->_error(self::ALL_EMPTY);
     return false;
    }

    if ($this->numberOfFieldsWithAValue() == 1) {
     return true;
    }

    if ($this->numberOfFieldsWithAValue() > 1) {
     $this->_error(self::MULTIPLE_VALUES);
     return false;
    }
}

private function valueIsNotEmpty() {
    return Zend_Validate::is($this->_value, 'NotEmpty');
}

private function numberOfFieldsWithAValue() {

    $fieldsWithValue = 0;

    foreach ($this->_fieldsToMatch as $fieldName) {

     if (isset($this->_context[$fieldName]) && Zend_Validate::is($this->_context[$fieldName], 'NotEmpty')) {
            $fieldsWithValue++;
     }
    }
    return $fieldsWithValue;
}

My solution is to either...

  • A. Let the developer figure out there is a certain way to do it.
  • B. Ignore $value, forcing you to pass all the elements (which isn't much different than the first option).
  • or C. (if possible) Find the name of the element that called my validator in the first place and ignore it from the list of $fieldsWithValue.

I don't think there is a way to apply a validator on a form without attaching it to an element, but that would be even better, if it were an option.

How can I solve this problem?

+1  A: 

Normaly i'd advise against such things, but, in this case I believe a static member in your class would actually provide a good solution to this problem.

With a static member, you can set it to the value in the first time the isValid is called, and check against it in subsequent calls, thus giving you a mechanism for this.

You may want to set this up to use some array in the configuration options, so that you can namespace and allow multiple instances of the validator to exist happily alongside each other for different sets.

The only problem that you really have to decide how to overcome, is where you wish to display the error, as yes the form itself does not take validators. if you want all the duplicates after the first to display an error, it is not so much of a problem.

Bittarman