Simoncpu Wiki
Advertisement

Overview[]

The validation system uses XML files to configure how input is validated. Validation is done before any action takes place.

Where are they located?[]

Module-specific validation classes are stored at %project_dir%/app/modules/%module_name%/lib/validators. The XML configuration files are stored at %project_dir%/app/modules/%module_name%/validate.

Global validation classes are stored at %project_dir%/app/lib/validator.

The XML configuration files has the following hierarchy:

   %project_dir%/app/config/validators.xml
                      |
                      |
                      V
   %project_dir%/app/modules/%module_name%/config/validators.xml
                      |
                      |
                      V
   %project_dir%/app/modules/%module_name%/validate/MyConfiguration.xml

Putting the common validation rules in the parent configuration file(s) prevents us from repeating them over and over again.

To set a configuration file's parent, simply define the parent attribute in <ae:configurations>:

<xml>
...
<ae:configurations
        xmlns="http://agavi.org/agavi/config/parts/validators/1.0"
        xmlns:ae="http://agavi.org/agavi/config/global/envelope/1.0"
        parent="%core.config_dir%/validators.xml"
>
...
</xml>

As an analogy, think of the configuration files in OOP terms: the parent XML files are like parent classes which child XML files inherit.

How are they named?[]

The filename for the XML file must be the same as your action name. For instance, if you are writing a validator for GatewaysEdit action, your XML file must be named as GatewaysEdit.xml.

Writing a Validator[]

  1. Write a class that extends the AgaviValidator class and implement a single method named validate(), that returns true on success and false on failure.
  2. Edit %project_dir%/app/modules/%module_name%/config/autoload.xml so that your validation class will be automatically loaded.
  3. ...

Please refer to the built-in validation classes at %agavi_dir%/validator for examples.

Writing an XML validation config file[]

Read requests (HTTP GET)[]

         <validators method="read">
               <validator class="Routing_RouteIDValidator">
                       <arguments>
                               <argument>id</argument>
                       </arguments>
                       <errors>
                               <error>Invalid ID.</error>
                       </errors>
               </validator>
       </validators>

Write requests (HTTP POST) with simple validation class example[]

In the X/HTML output:

<html>
...
    <form action="/this/is/an/example" method="post">
        <input type="text" name="form_element" id="form_element" />
        <input type="submit" name="ok" id="ok" value="Submit" />
    </form>
...
</html>

In the XML config file:

<xml>
...
        <validators method="write">
                <validator class="MyValidator">
                        <arguments>
                                <argument>form_element</argument>
                        </arguments>
                        <errors>
                                <error>Default error message.</error>
                                <error for="min">The input is less than 3.</error>
                                <error for="max">The input is more than 8.</error>
                        </errors>
                </validator>
        </validators>
...
</xml>

In the MyValidator.class.php file:

<?php

class MyValidator extends AgaviValidator
{
    protected function validate()
    {
        $value = $this->getData($this->getArgument());
        if ($value < 3) {
            $this->throwError('min');
            return false;
        } else if ($value > 8) {
            $this->throwError('max');
            return false;
        } else {
            $this->throwError();
            return false;
        }

        return true;
    }
}

?>

Whenever a validation error occurs, the previous wrong value and applicable error messages will be inserted into the XHTML output without any additional effort on your part:

<html>
...
    <form action="/this/is/an/example" method="post">
        <input type="text" name="form_element" id="form_element" value="1024" />
        <div class="errors"><p class="error">The input is more than 8.</p></div>
        <input type="submit" name="ok" id="ok" value="Submit" />
    </form>
...
</html>

Cool, huh?

Note that instead of directly using "this/is/an/example", it is generally a good idea to let Agavi generate the correct URL from the routes instead.

My class name is too long. Can I use a shortcut?[]

Yes. In fact, it's a good idea to map applicable validation classes to specific types of data:

<xml>
...
        <validator_definition name="IPv4" class="MyIPv4AddressValidator" />
        <validator_definition name="IPv6" class="MyIPv6AddressValidator" />
        <validator_definition name="domain" class="MyDomainNamesValidator" />

        <validators method="write">
                <validator class="IPv4">
                        <arguments>
                                <argument>address</argument>
                        </arguments>
                        <errors>
                                <error>Invalid IPv4 address.</error>
                        </errors>
                </validator>
        </validators>
...
</xml>

Definitions are usually defined in the parent XML configuration file. As an analogy, think of parent XML configuration files as header files that you #include in your C code. Definitions are like typedefs.

Please refer to %agavi_dir%/config/defaults/validators.xml for examples.

Form Population Filter[]

Whenever a validation error occurs, Agavi automatically fills the form with the previous values.

How does this work?

Agavi treats the output as XML and manipulates it. It manipulates the structure of the form and appends the relevant error messages to the form elements.

In light of this, the output has these restrictions:

  • The document must be a valid X/HTML. Unlike in HTML, incorrect markup will no longer work.
  • XML doesn't like HTML entities such as &nbsp; and &copy;
    • We need to use numerical entities instead (&copy; => &#169;)
    • General recommendation is to use the UTF-8 charset and use the actual character instead (i.e., use ©). As the world is getting smaller each day, we'll probably need to use Unicode to translate our products into different languages anyway.

Quirks and Caveats[]

Validating the Same Fields with "or" Class[]

<validator class="or">
        <validators>
                <validator class="FooBar" required="false">
                        <arguments>
                                <argument>MyField</argument>
                        </arguments>
                </validator>
                <validator class="BlahBlah" required="false">
                        <arguments>
                                <argument>MyField</argument>
                        </arguments>
                </validator>
        </validators>
        <errors>
                <error>This is an error.  Blah</error>
        </errors>
</validator>

What happens:

FooBar = success
BlahBlah = failure

MyField's value is returned to the action and everything proceeds as expected.

FooBar = failure
BlahBlah = success

MyField's value is not returned to the action because the whole validation is marked as failure.

TODO: put clearer explanation

Solution

Write a custom validator instead:

<validator class="FooBarOrBlahBlah" required="false">
        <arguments>
                <argument>MyField</argument>
</arguments>
</validator>
<errors>
        <error>This is an error.  Blah</error>
</errors>

Links[]

Advertisement