ZF-10919: Zend_Form_Element - BreakChainOnFailure

Description

I think this is a bug, but perhaps it's by design. It certainly did not work as I expected.

  1. I have to change the error message of "NotEmpty" validator for a Zend_Form_Element.
  2. There are multiple elements that need the same message change.
  3. I create a custom Zend_Form class.
  4. I create $notEmpty = new Zend_Validate_NotEmpty() instance.
  5. I set the custom message with $notEmpty->setMessage($message, $code);
  6. I add that validator to every element that needs is with "$element->addValidator($notEmpty);"
  7. On the second or later element, I change the "breakChainOnFailure" parameter to true.
  8. The "breakChainOnFailure" value is not not updated.

Here's some sample code: (inside a Zend_Form class constructor) {quote} 1 : $notEmpty = new Zend_Validate_NotEmpty(); 2 : $notEmpty->setMessage('Sorry, that this value is empty.',Zend_Validate_NotEmpty::IS_EMPTY); 3 : 4 : $element_name = new Zend_Form_Element_Text('name'); 5 : $element_name->setRequired(true); 6 : $element_name->addValidator($notEmpty); 7 : 8 : $element_email = new Zend_Form_Element_Text('email'); 9 : $element_email->setRequired(true); 10 : $element_email->addValidator($notEmpty, true); 11 : $element_email->addValidator('EmailAddress'); {quote}

Issue $element_email will not break chain on failure. The "true" parameter gets ignored and I believe this is why:

/Zend/Form/Element.php line 1134

{quote} 1134 : if ($validator instanceof Zend_Validate_Interface) { 1135 : $name = get_class($validator); 1136 : 1137 : if ( !isset( $validator->zfBreakChainOnFailure ) ) { 1138 : $validator->zfBreakChainOnFailure = $breakChainOnFailure; 1139 : } 1140 :} {quote}

Passing $notEmpty validator object into the first Zend_Form_Element will hit the code above. Once the object hits line #1137, it evaluates to true(not set) because there is no "zfBreakChainOnFailure" in Zend_Form_Element or Zend_Form_Element_Abstract and goes into line #1138 where it sets this value to false because that was the original (default) value of "$element_name" at line #6 above.

When "$element_email" tries to set this validator, it goes to the line # 1137 but this time it evaluates to false(set) because this object was set by the previous element.

Because of this, it ignores the passed in parameter.

I marked this as minor because I didn't think it was "major" and you can: A) Create a new Validation object for each element, even using the "string name" method and passing in the new error messages each time. But this replicated a lot of code. B) Create a $notEmpty2 object for the "breakChainOnFailure" elements that are set to true and use the appropriate object on the appropriate element. This, again, replicates code but a lot less than before.

Maybe this is how it's suppose to work, but it doesn't seem like this is how it should work.

I would think that I should be able to create a single validation object with custom error messages and selectively tell what elements to break the chain and which not to, but not have to create a new Validation object for every case, repeating code.

It appears that since the "break chain" attribute is associate with the Validation object via "zfBreakChainOnFailture" that removing the "isset()" conditional would not work because the object would, instead, be set to whatever was the last element to set it, instead of whatever was the first.

Instead, perhaps the "break chain" attribute should be an element level data point, not a validator level data point. Meaning, the element could remember if it should break chain on a given validator, not the validator itself.

But I have not though that through thoroughly, so there might be better reasons why it is the way it is.

Cheers!

Comments

+1