Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

Zend Framework: Zend_Validate error messages Component Proposal

Proposed Component Name Zend_Validate error messages
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_Validate error messages
Proposers Bill Karwin
Revision 1.1 - 13 May 2007: Initial write-up. (wiki revision: 11)

Table of Contents

1. Overview

This is a proposal to add to Zend_Validate classes the ability to specify error messages.

2. References

  • TBD

3. Component Requirements, Constraints, and Acceptance Criteria

  • This component will provide a method in Zend_Validate classes to allow developers to specify the text for error messages
  • This component will allow messages to contain placeholder text, to be replaced by specific properties of the validator class.
  • This component will not allow arbitrary access to any protected properties of the validator class, only the ones specifically identified as legal to interpolate into the message templates.
  • This component will support multiple messages per validator class; the messages are identified by mnemonic keys; developers can specify multiple messages in one call.
  • This component will have default messages in each validator; it is not required to specify messages.
  • This component will accept a message template without a message key; there is a default message.

4. Dependencies on Other Framework Components

  • Zend_Exception

5. Theory of Operation

This solution requires a change to Zend_Validate classes, to store error messages in a class member instead of in hard-coded text as the error conditions are met. Since the messages are stored, their value can be changed.

Since a given validator class can generate more than one message, the interface to change a value of a messages requires that the messages be identifiable. We use a string key to identify the error message. Then the developer can change the value using a method setMessage($messageText, $key). The $key argument is optional, and defaults the first message stored in the class. This makes usage easy if the validator is simple enough to have only one message.

There is also a method setMessages(array) so you can pass an associative array to set all messages by key.

When the validator's isValid() method returns false, you can call getMessages() in the familiar way to get one or more messages related to the failure. This returns either message text that the developer specified in the setMessage() method.

In addition, this proposal provides a new method getErrors() which returns an array of strings which are taken from the key values of the associative array. Thus the validator can return both developer-specified messages, and standard key strings.

6. Milestones / Tasks

  • Milestone 1: design notes will be published here
  • Milestone 2: Working prototype checked into the incubator supporting use cases #1, #2, ...
  • Milestone 3: Working prototype checked into the incubator supporting use cases #3 and #4.
  • Milestone 4: Unit tests exist, work, and are checked into SVN.
  • Milestone 5: Initial documentation exists.

7. Class Index

  • Zend_Validate_Abstract
  • Zend_Validate_<All>

8. Use Cases

UC-01

Using a single validate class, overriding the message, and re-running the validation.

UC-02

Usage from proposed Zend_Filter_Input.

Zend_Validate_Abstract
Zend_Validate_LessThan

This is a sample of utilizing Zend_Validate_Abstract.

9. Class Skeletons

]]></ac:plain-text-body></ac:macro>

]]></ac:plain-text-body></ac:macro>

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. May 14, 2007

    <p>This is a fairly intelligent solution. It makes the error messages in the validators overridable, which is a big improvement. However, my major qualm with this approach is the fact that it doesn't really encourage removing the error messages from the validators completely.</p>

    <p>I believe that the error messages should be strictly confined to the View layer of the MVC paradigm; that's just my philosophical stance on the issue. Providing the user-settable error handler function, as I mentioned in the Zend_Validate_Builder proposal, has a couple of advantages:</p>
    <ul>
    <li>Separates the two distinct responsibilities of error message management and data validation</li>
    <li>Allows the two separated responsibilities to remain loosely coupled and still work together</li>
    <li>Does not require Controllers to know the error messages</li>
    <li>Allowing error messages to live outside of validators would be a boon to i18n efforts</li>
    <li>Implementing this in the current validators would take approximately the same amount of work as implementing the changes proposed here</li>
    </ul>

    <p>By the time I get done with the Zend_Validate_Builder proposal, there will be a few examples of this usage within the Use Cases code. Hopefully that will illustrate what I'm talking about a bit better.</p>

  2. May 14, 2007

    <p>It probably should be mentioned here that the purpose of the validation messages produced by classes implementing Zend_Validate_Interface is simply to indicate to the application the specific reason(s) why a particular datum fails the encapsulated validity tests. These validation messages are completely separate from messages that an application user sees, unless a developer specifically codes it that way.</p>

    <p>For example, consider an instance of Zend_Validate_Between, having a minimum of 10 and a maximum of 20. Assuming that the validation is performed inclusively, any given input datum may fail validation for either of the following two reasons:</p>

    <ol>
    <li>the input datum is less than 10</li>
    <li>the input datum is greater than 20</li>
    </ol>

    <p>Zend_Validate_Between does not know whether or not such a distinction is important to its consumer, so it provides the distinction through the validation messages.</p>

    <p>Given that it may be important to the consumer to know the particular reason that a validation failed, having a class constant to identify each possible message would facilitate branching, logging, and statistical reporting while also supporting code completion by editors.</p>

    1. May 14, 2007

      <p>I have updated the proposed code so that both the immutable "reason" for the error (in the form of the key of the <code>$_messageTemplates</code>) is available, in addition to the user-specified message text.</p>

      <p>A given validator class can provide class constants if desired.</p>

      <p>I think there's a disconnect in the requirements, though: Darby, you and Bryce are saying (and I agree) that the app developer should not use the error message returned by the validator verbatim, but the developer should use the state (pass/fail) returned by <code>isValid()</code> and then implement their own application-specific output. I think this is a preferable practice, but the continuing demand for Zend_Validate classes to allow the developer to override the message clearly shows that this is <strong>not</strong> how people want to use the class. </p>

      <p>They want the validator class to return the exact message to display in the app. They don't want a callback, and they don't want to implement it themselves. I think for the sake of the "extreme simplicity" goal of Zend Framework, that we should provide this functionality.</p>

      1. May 15, 2007

        <p>I appreciate what you're saying, Bill. You're in a much better position to gauge the "demand" for any particular feature of the framework than I.</p>

        <p>I don't really have a problem with the validators having default error messages that are overrideable. But I really think that the most important aspect of the errors raised in the validators is the direct reason itself. It seems to me that the framework should be designed in such a way as to at least <strong>allow</strong> the developer to not rely on the default error messages. That is, the message part should be optional, and the reason key should be required, and what the internal framework code relies upon. </p>

        <p>As an aside, where do you guys think that the error messages <strong>should</strong> live? I feel like the Controllers shouldn't ever even know about the error messages, that they are strictly a View concern. Developers wanting to override the error messages at the time they create validators are most likely going to have hard-coded error messages throughout their Controller code. To me, that seems like a bad practice to encourage, and would make thinks like i18n very difficult. Another, perhaps conflicting, ZF goal is to encourage best practices, right?</p>

        1. May 15, 2007

          <p>I have updated the sample code above to show some usage of a <code>getErrors()</code> method. When a validator fails, it sets the errors to an array of strings, which are keys of the <code>$_messageTemplates</code> array. These strings are defined in the class, and cannot be overridden. So this provides both a developer-configurable solution for overriding messages, as well as an immutable solution.</p>

          <p>I have also shown how the custom error messages can be set using the proposed Zend_Filter_Input solution. I have this coded and unit tested, I'm just waiting for approval of this proposal to commit.</p>

  3. May 16, 2007

    <p>I can understand why error messages are hell bent on being integrated into the Validate classes, but I really don't agree with it. The more focus this gets, the more the classes are being pushed into that corner. A lot of the user expectation is based on previous iterations - not current proposals for a better way. If messages stay then we're effectively giving the validator classes a dual role.</p>

    <p>It's not a good way of handling user defined messages. A validator need only return a boolean, and offer an interface to collect other information useful in composing errors. If you need access to passed in values use the original array (or allow something in the validator class itself). If you need ranges from the Validator (e.g. params to Zend_Validate_Between) object maybe set the parameters as public fields (any reason not to?) and use get_object_vars(). The more data is accessible, the easier it is to apply i18n using a translation object and sprintf() externally.</p>

    <p>sprintf() leads onto the proposed %value% syntax for str_replace. I might have read this wrong (happens unfortunately <ac:emoticon ac:name="sad" />). Take an example of Zend_Validate_Between. Where do I specify the second value? What if a custom validator takes three or more parameters, all needing substitution into the message? What if I later edit a message, with three substituted values, and need to change the substitution order? Knowing the parameter order before hand, sprintf() can allow all of those since its placeholders specify order placement.</p>

    <p><em>As an aside, where do you guys think that the error messages should live? I feel like the Controllers shouldn't ever even know about the error messages, that they are strictly a View concern. Developers wanting to override the error messages at the time they create validators are most likely going to have hard-coded error messages throughout their Controller code. To me, that seems like a bad practice to encourage, and would make thinks like i18n very difficult. Another, perhaps conflicting, ZF goal is to encourage best practices, right?</em></p>

    <p>Controllers and Views are in the same layer of the application. Granted there is separations for obvious reasons, but sometimes it's just not realistic. I certainly don't want Validator objects swimming around in the View.</p>

    <p>Does Controller based messaging make i18n more difficult? Maybe a bit. Depends on how you approach it. The simplest is just to strip the messages to a separate configuration file as a list of translation keys (i.e. sprintf formatted with placeholders). Then all the controller need do is worry about passing the right string, from the configuration, to a suitable translation object - from there the View is a step away.</p>

    <p>Of course I go configuration crazy on everything <ac:emoticon ac:name="smile" />. I would love to see a Factory for creating and confuring Validator/Filter objects from an XML/YAML definition. It's inevitable either you guys propose such, or I do <ac:emoticon ac:name="wink" />.</p>

    <p>Sorry for the overly long comment!</p>

    1. May 17, 2007

      <p>I agree - the validation error messages should be application-driven, not validator-driven. However, this idea has not been embraced by people who want to use the validator component. </p>

      <p>The question of how to override the error messages has come up several times on the mailing lists. We answer that one should just use the boolean result and create application-specific messages yourself. This suggestion has been rejected several times. People want to specify their error messages in a declarative way, not by writing code in callback functions.</p>

      <p>So this proposal is trying to give a solution to the usage that people expect. The guiding principle of Zend Framework is "extreme simplicity". If we can employ best practices along the way, that's great, but as I understand it, the priority is on simplicity.</p>

      <p>Regarding your point about sprintf(), it seems easier to use the str_replace() solution, since this does not require that the developer knows anything about the order of parameters. A sprintf() solution does require this. I implemented the str_replace() solution with syntax similar to the message template format in Zend_Log.</p>

      <p>The message identifiers, as well as the keys that can be used in a given validator, must be defined in the validator class. Then you can use them in the error message, for example:</p>

      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      $btwn = new Zend_Validate_Between(1, 12);
      $btwn->setMessage('Value %value% must be between %min% and %max%', 'notBetween');
      ]]></ac:plain-text-body></ac:macro>

      <p>Regarding XML, Andi has made it clear many times that any proposal to the Zend Framework involving the use of an XML configuration file will be auto-rejected. <ac:emoticon ac:name="smile" /> This is a design decision that resists the <em>extreme complexity</em> caused by using XML files in things like J2EE and Spring.</p>

      <p>Regarding driving the validator/filter logic from a config spec, the Zend_Filter_Input design I've been working on provides a way to represent the filter/validator rules as data, in the form of a PHP array definition. This is pretty much equivalent to storing the rules in a config file, and one could in fact do that.</p>

      <p>Also, the Zend_Filter_Input class does provide the equivalent of a Factory pattern, even though it doesn't have a method called <code>factory()</code>. It creates instances of filters and validators based on data in that array. Yesterday I added support for declaring objects with their arguments, so there's almost no need to use "<code>new</code>" anymore.</p>

  4. May 22, 2007

    <p>I'd like to propose a new interface method, hasError($value). It would be the antithesis to isValid. It will return false if the value is valid, otherwise it will return the reason(s) code for failure.</p>

    <p>Benifits:</p>
    <ul class="alternate">
    <li>One call to validate and obtain reason.</li>
    </ul>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    if ($reason = $validator->hasError($value))
    {
    // failure
    }
    ]]></ac:plain-text-body></ac:macro>

    <p>The purpose of such a change would allow building a class like Zend_Validate_Builder or Zend_Filter_Input that could instantiate one Zend_Validate_? class and use it across multiple fields. Also, I like the aspect of pushing the responsibility of error messages out to a parent class.</p>

    <p>thoughts?</p>

    1. May 22, 2007

      <p>This seems like a nice simplification to the API, and it should improve performance as a nice side effect. </p>

  5. May 23, 2007

    <ac:macro ac:name="note"><ac:parameter ac:name="title">Zend Comment</ac:parameter><ac:rich-text-body>
    <p>This proposed solution is approved for development.</p>

    <ul class="alternate">
    <li>Please make a solution for both developer-specified messages and immutable error identifiers.</li>
    <li>Please make getters for parameters that can be included in messages, so they can be accessed as properties.</li>
    <li>Please make a method to access error identifiers, in addition to error messages.</li>
    </ul>
    </ac:rich-text-body></ac:macro>

  6. Feb 11, 2008

    <ac:macro ac:name="unmigrated-wiki-markup"><ac:plain-text-body><![CDATA[    $project_title = $_POST['project_title'];

        // Create a validator chain and add validators to it
        $validatorChain = new Zend_Validate();
        $validatorChain->addValidator(new Zend_Validate_StringLength(3, 100))
                   ->addValidator(new Zend_Validate_Alnum());

        // Validate the username
        if (!$validatorChain->isValid($project_title))

    Unknown macro: {        print join('<br />', $validatorChain->getMessages());    }

    It will be better to have the field name that the validator is checking because if you have 5-10 fields you will know how to relate error messages.]]></ac:plain-text-body></ac:macro>

    1. Feb 11, 2008

      <ac:macro ac:name="unmigrated-wiki-markup"><ac:plain-text-body><![CDATA[This is crazy. Why the Wiki does not have edit enabled ?
      It gives me Macro not found when I try to use join....

          $project_title = $_POST['project_title'];

          // Create a validator chain and add validators to it
          $validatorChain = new Zend_Validate();
          $validatorChain->addValidator(new Zend_Validate_StringLength(3, 100))
                     ->addValidator(new Zend_Validate_Alnum());

          // Validate the username
          if (!$validatorChain->isValid($project_title))

      Unknown macro: {        var_dump($validatorChain->getMessages());    }

      It will be better to have the field name that the validator is checking because if you have 5-10 fields you will know how to relate error messages.]]></ac:plain-text-body></ac:macro>