As a holiday song reminds us, we should be "...making a list, checking it twice." Client-side validation is the first check. Server-side validation is the second. And an excellent module to help us with server-side validation is cbvalidation.
cbvalidation
cbvalidation lets you define constraints
to ensure a given target object or struct meet. Constraints can be defined
in three places: shared constraints in your config/ColdBox.cfc
file, model
constraints on the target component to validate, and ad hoc constraints in the
validation method itself. Valid targets are components and structs.
Regardless of where the constraints are stored, they take the same shape:
// as well as storing these on `this.constraints` // this could be a shared constraint defined in `config/ColdBox.cfc` // or a struct passed directly to a validate method. this.constraints = { "firstName" = { "required" = true }, "lastName" = { "required" = true }, "email" = { "required" = true, "type" = "email" }, "age" = { "required" = false, "type" = "numeric", "min" = 0 } };
Validating Models
cbvalidation can validate any getters on an component, that is any method that
follows the "get" & propertyName
convention. (This includes properties on
your component when accessors="true"
is defined.) In your constraints, just
reference the property name.
The most common place to store these constraints is on the component itself,
under a this.constraints
key. cbvalidation will use this struct by convention
when validating the model. This means your validation for a user component
can be as simple as validateModel( user );
.
Validating Structs
Some times you need to validate a struct, usually the rc
scope in ColdBox.
An example of this is changing passwords. Usually a user is asked to type in
the same password twice to ensure they are typing it in correctly. cbvalidation
has a validator to help with that!
{ "password" = { "required" = true }, "passwordConfirmation" = { "required" = true, "sameAs" = "password" } }
Those validation rules will do nicely. But where do we store this? Storing on
the model itself is not a good idea because the passwordConfirmation
field is
required
in this instance. When we are updating user data like normal, we won't
want to enforce the passwordConfirmation
field being required. This is a
good use case to use ad-hoc validation:
var result = validateModel( target = rc, constraints = { "password" = { "required" = true }, "passwordConfirmation" = { "required" = true, "sameAs" = "password" } } );
This validation call would go right in the handler where updating a password happens in your application, solving the problem of validation that is only needed for certain actions in your application.
Shared Constraints
If your validation constraints don't belong on the model because they are not used every time, but they are used in multiple places still, a shared constraint may be the way to go.
Shared constraints are defined in your config/ColdBox.cfc
under a
validation.sharedConstraints
key.
validation = { sharedConstraints = { "changedPassword" = { "password" = { "required" = true }, "passwordConfirmation" = { "required" = true, "sameAs" = "password" } } } };
Now the example from earlier is reduced down to this:
var result = validateModel( target = rc, constraints = "changedPassword" );
Now we don't have to repeat ourselves all over our application with the same validation rules while staying flexible with what rules we run at what times.
Custom Messages
Error messages can be customized by providing a key of the validation name
suffixed with message
, for instance requiredMessage
for the required
validation. These messages can reference special tokens which differ per
validation type. Check out the docs
for all the different tokens.
{ "username" = { "required" = true, "requiredMessage" = "You forgot to enter your {field}" // will output "You forgot to enter your username" } }
Custom Validators
Lastly, you can specify your own custom validators, a component that returns
true
or false
depending on if the validation passes.
You can register a simple custom validator with the validator
argument.
{ "username" = { "required" = true, "validator" = "UniqueValidator@cborm" } }
If you need custom validators with properties or multiple custom validators, you can also pass in a WireBox slug as the validator name:
{ "username" = { "required" = true, "ExistsInDB" = { "table" = "users", "column" = "username" } } }
Wrap Up
As you can see from the post length, this module is fully featured and ready to tackle all your validation needs in you application. Good luck and happy validating!
Add Your Comment