Cocoon Forms: Validation
Concept
For each widget you can define a number of widget validators. A widget validator can perform some checks on the widget and if these fail, set a validation error on the widget.
Implementation-wise, a widget validator is an object implementing the WidgetValidator interface (javadoc).
WidgetValidators can either be defined as part of the form definition (inside the fd:validation element) or added to a widget instance at runtime. The former is useful for validators that only act on the data in the form. The latter is useful if the validation logic needs access to other objects you have available in your controller.
The validation logic runs over the widget tree. For each widget first the child widgets are validated and then the widget itself (recursively). The validators on a widget are executed in the order in which they were defined or added. First the ones defined in the form definition are executed, then the ones added on the widget instance. The validation of a widget stops at the first validator that fails (but continues to execute for the other widgets).
For widgets having a datatype and hence a convertor (field and multivaluefield), the convertor could be considered to be the first validator, i.e. it is executed before the other validators (because those operate on the converted value). If the conversion fails a validation error is set on the widget.
Validation errors can only be set on widgets implementing the interface ValidationErrorAware, which currently is not implemented by all widgets. For example, a repeater widget does not implement ValidationErrorAware. However, a validator attached to a repeater could perform inter-row checks on the fields in the different rows of the repeater, and set validation errors on these fields (instead of on the repeater itself).
CForms supplies a number of default widget validators, mostly for performing checks on the value of field widgets. Additionally you can write your own ones in Java or in Javascript.
Table of widgets supporting ValidationErrorAware
These are the widgets on which you can call setValidationError (and getValidationError). This is relevant if you are writing your own validation logic.
Widget |
Supports ValidationErrorAware |
---|---|
field |
|
multivaluefield |
|
booleanfield |
|
repeater |
|
output |
|
submit |
|
action |
|
repeater-action |
|
row-action |
|
aggregatefield |
|
upload |
|
messages |
Reference
General remarks
For most widget validators, the failmessage (i.e. the message displayed to the user in case the validation failed) can be overridden by specifying a child fd:failmessage element inside the validator element. The failmessage can contain mixed content. Example:
<fd:field id="yourmail"> <fd:datatype base="string"/> <fd:validation> <fd:email> <fd:failmessage>Not a valid email address!</fd:failmessage> </fd:email> </fd:validation> </fd:field>
To provide locale-dependent messages, use i18n tags in combination with the I18nTransformer.
Often the values that validators will check are specified as expressions. CForms uses for this the xReporter expression interpreter.
Note that you cannot use each validator with each widget. Most validators only work with certain types of widgets, in case of field widgets often expecting a specific datatype. The below table shows the supported combinations for the default validators.
Validator |
Allowed datatypes |
---|---|
fd:assert |
all datatypes |
fd:email |
string |
fd:length |
string |
fd:mod10 |
string |
fd:range |
integer, long, decimal |
fd:regexp |
string |
fd:value-count |
all array types (use this with multivaluefield) |
fd:java |
all datatypes |
fd:assert
Evaluates the expression specified in the "test" attribute. This expression should have a boolean result, it should evaluate to either true or false. Example: Suppose there are 2 fields widgets password and confirmPassword. We can use assert inside confirmPassword to check if is equals to password widget:
<fd:assert test="password = confirmPassword"> <fd:failmessage>The two passwords are not equal.</fd:failmessage> </fd:assert>
fd:email
Checks that a value is a valid email address. Example:
<fd:email/>
Currently this checks the email does not contain any spaces, contains exactly one @ symbol with at least one character before it and at least one dot after it.
fd:length
Checks the length of strings. This validator can take 3 attributes: min, max and exact. You can use either of these three separately or min and max together. The values of these attributes are expressions. Example:
<fd:length min="2" max="4"/> Another example: <fd:length exact="2*2"> <fd:failmessage>Must be 4 characters long!</fd:failmessage> </fd:length>
fd:mod10
Uses the "mod10" algorithm used to check the validity of credit card numbers such as VISA. This validator does not require any additional attributes. Example:
<fd:mod10> <fd:failmessage>Invalid credit card number.</fd:failmessage> </fd:mod10>
fd:range
Checks the numeric range. This validator can take 3 attributes: min, max and exact. You can use either of these three separately or min and max together. The values of these attributes are expressions. Example:
<fd:range min="2" max="4"/> Another example: <fd:range exact="2*2"/>
fd:regexp
Checks that a string matches a regular expression. It requires a "pattern" attribute specifying the regexp. The regular expression library used is Jakarta ORO, see here for some information. Example:
<fd:regexp pattern="[a-z]{3,5}"> <fd:failmessage>Invalid code!</fd:failmessage> </fd:regexp>
fd:value-count
Checks the number of items selected in a multivaluefield. Again works with min, max and exact attributes. Example:
<fd:value-count min="2" max="4"/> Another example: <fd:value-count exact="2"/>
fd:javascript
Allows to write a validator using Javascript, embedded directly in the form definition. The widget in question is available in the Javascript snippet as a variable called widget.
See the samples of Cocoon for an example.
fd:java
Allows to write a validator as a java class. The class must implement the the WidgetValidator interface (javadoc) and be deployed in the cocoon webapplication.
<fd:java class="com.mycompany.validators.MyCustomValidator"/>
For an example, see the class org.apache.cocoon.forms.samples.CustomBirthDateValidator included with Cocoon. Please note that, as defined by the WidgetValidator contract, the custom validator must add a validation error to the widget; simply returning false will not display an error to the user.
The validator instance is part of the form definition and hence shared between multiple form instances. This means it should usually not keep any state in its instance variables.
The validator class can optionally implement Avalon interfaces such as LogEnabled, Serviceable and Contextualizable to get access to the logger, service manager and context respectively. Via the context, you can get access to the request object (using the ContextHelper utility class of Ccooon).
As an alternative for fd:java, you can also add validators at run time to a form instance, using the addValidator method on a form or widget. Since you then instantiate the validator yourself, you can pass it anything you want, and it can be stateful.