Thanks to Dan Vega’s incredible work with Hyrule and inspirations from Grails, rails, django, and form daddy. We now have a full fledged server side object validation framework. It requires CF9 or above, or Railo 3.1 or above.
Our documentation will be out soon but here are a few pointers. We have based it on several intefaces, so you can use any validation framework in your ColdBox applications as long as they implement the interfaces. Hyrule is the easiest to port. We have also allowed for you to declare constraints in your configuration file so you can reuse validation constraints or as we call them shared constraints.
Todo:
- More wide testing
- i18n for messages. Right now it uses generic messages.
- Custom messages. Ability to declare a custom message constraint.
Configuration
In your ColdBox.cfc you can now declare the following:
validation = {
manager = “class path”,
sharedConstraints = {
sharedName = { constraints }
}
}
The manager is the class path of the validation manager you woud like to use. By default you do not set it as it defaults to ‘coldbox.system.validation.ValidationManager’
Then you can optional register shared constraints. This means you register them with a unique sharedName of your choice and it’s value is a collection of constraints for properties in your objects. Here is an example:
sharedConstraints = {
user = {
fName = {required=true},
lname = {required=true},
age = {required=true, max=18 }
metadata = {required=false, type=“json”}
}
}
As you can see, our constraints definition describes the set of rules for a property. Once your application loads, the shared constraints and your validation manager are configured.
Event Handlers
You now have the ability to use the following functions:
getValidationManager() or controller.getValidationManager()
validateModel(target, [fields], [constraints] )
You pass in your target object, an optional list of fields or properties to validate only (by default it does all of them), an an optional constraints argument which can be the shared name or an actual constraints structure a-la-carte. If no constraints are passed, then we will look for the constraints in the target object as a public property called ‘constraints’.
component{
this.constraints = {
fName = {required=true},
lname = {required=true},
age = {required=true, max=18 }
metadata = {required=false, type=“json”}
}
}
The validateModel() method returns coldbox.system.validation.result.IValidationResult type object, which you can use methods like:
/**
- Determine if the results had error or not
-
@field.hint The field to count on (optional)
*/
boolean function hasErrors(string field);
/**
- Clear All errors
*/
coldbox.system.validation.result.IValidationResult function clearErrors();
/**
- Get how many errors you have
-
@field.hint The field to count on (optional)
*/
numeric function getErrorCount(string field);
/**
- Get the Errors Array, which is an array of error messages (strings)
-
@field.hint The field to use to filter the error messages on (optional)
*/
array function getAllErrors(string field);
/**
- Get an error object for a specific field that failed. Throws exception if the field does not exist
-
@field.hint The field to return error objects on
*/
coldbox.system.validation.result.IValidationError[] function getFieldErrors(required string field);
/**
- Get a collection of metadata about the validation results
*/
struct function getResultMetadata();
/**
- Set a collection of metadata into the results object
*/
coldbox.system.validation.result.IValidationResult function setResultMetadata(required struct data);
The Error Interface: coldbox.system.validation.result.IValidationError is also very simple and has these useful methods for building your error responses:
/**
- Get the error message
*/
string function getMessage();
/**
- Get the error field
*/
string function getField();
/**
- Get the rejected value
*/
any function getRejectedValue();
/**
- Get the error representation
*/
struct function getMemento();
So you can do:
prc.validationResults = validateModel( obj );
if( prc.validationResults.hasErrors() ){
// do something here
getPlugin(“MessageBox”).error( prc.validationResults.getAllErrors() );
}
You can even use the results object in your views to get specific field errors, messagesbox, etc.
WireBox DSL
You can use now → coldbox:validationManager to get a reference to the validation manager.
Available Constraints
The available constraints are:
propertyName = {
// required field or not, includes null values
required : boolean [false],
// specific type constraint, one in the list.
type : (ssn,email,url,alpha,boolean,date,usdate,eurodate,numeric,GUID,UUID,integer,string,telephone,zipcode,ipaddress,creditcard,binary,component,query,struct,json,xml),
// size or length of the value which can be a (struct,string,array,query)
size : numeric or range, eg: 10 or 6…8
// range is a range of values the property value should exist in
range : eg: 1…10 or 5…-5
// regex validation
regex : valid no case regex
// same as another property
sameAs : propertyName
// same as but with no case
sameAsNoCase : propertyName
// value in list
inList : list
// discrete math modifiers
discrete : (gt,gte,lt,lte,eq,neq):value
// UDF to use for validation, must return boolean accept the incoming value and target object, validate(value,target):boolean
udf = variables.UDF or this.UDF or a closure.
// Validation method to use in the target object must return boolean accept the incoming value and target object
method : methodName
// Custom validator, must implement coldbox.system.validation.validators.IValidator
validator : path or wirebox id, example: ‘mypath.MyValidator’ or ‘id:MyValidator’
// min value
min : value
// max value
max : value
}
Custom Validators
As you can see, you can build also your own validators by implementing our interface coldbox.system.validation.validators.IValidator, then you can put the path or a registered wirebox id.
/**
- Will check if an incoming value validates
- @validationResult.hint The result object of the validation
- @target.hint The target object to validate on
- @field.hint The field on the target object to validate on
-
@targetValue.hint The target value to validate
*/
boolean function validate(required coldbox.system.validation.result.IValidationResult validationResult, required any target, required string field, any targetValue, string validationData);
/**
- Get the name of the validator
*/
string function getName();