Validation in ColdBox 3 with ValidateThis
Two frameworks that I like to use on the majority of applications I write are ColdBox 3 and ValidateThis. ColdBox is an MVC framework on steriods and ValidateThis is, as the name suggests, a validation framework. As I was asked about this recently, I thought I'd blog a simple example of how you use them together.
I'm going to assume that you're familiar with ColdBox, if you're not then there is excellent documentation available at http://wiki.coldbox.org/. At the time of writing ColdBox 3 is at the Release Candidate stage and is available from https://github.com/ColdBox/coldbox-platform. ValidateThis (the current version is 0.981) can be downloaded from http://validatethis.riaforge.org/
I like to keep all my code, including frameworks, in the web root, so that it is as portable as possible and I don't have to worry about using different versions of frameworks on different development projects. To do this, I create a "frameworks" folder in the root of the application and sub folders for coldbox and validatethis where I extract the downloaded zips. I also use set up ColdBox using the "bootstrapper" approach, so that Application.cfc does not extend coldbox.system.Coldbox. My directory structure looks like this:
/config
/frameworks
/coldbox
/validatethis
/handlers
/plugins
/layouts
/views
/model
/Application.cfc
/index.cfm
I then set up per-application mappings in Application.cfc like so:
this.mappings["/coldbox"] = COLDBOX_APP_ROOT_PATH & "frameworks/coldbox/";
this.mappings["/validatethis"] = COLDBOX_APP_ROOT_PATH & "frameworks/validatethis/";
A quick word of warning; if you're developing an ORM enabled application and you've extracted the ValidateThis download inside your webroot, then you'll need to either delete the samples directory or use the ormsettings.cfclocation to define where your entities are (I tend to delete the samples directory and set the cfclocation path) like so:
this.ormsettings.cfclocation = "model";
Now that the ColdBox skeleton applicaton is up and running, it's time to integrate ValidateThis. The easiest way is to use the ValidateThisCB3Plugin.cfc which you can find in the extras directory of the ValidateThis download. Copy this cfc to the plugins directory of your ColdBox application. Don't worry about the ColdBoxRBTranslator.cfc file, we don't need that at the moment. Hopefully I'll find time to blog about what that's for soon. The ValidateThisCBPlugin.cfc (note, no 3 in the filename) is for use with ColdBox 2.6.
ValidateThis has several configuration options, which you define in Coldbox.cfc, but ValidateThis will work with ColdBox out of the box with zero configuration. So let's get started and do some validation for a simple task manager. ValidateThis can validate ORM entities, objects or structs, but I'm going to demonstrate with an ORM task entity.
/**
* I model a task
*/
component persistent="true" table="tasks"
{
property name="taskID" column="idtasks" fieldType="id" generator="native";
property name="title" ormtype="string";
property name="comments" ormtype="string";
}
We need a tasks handler.
/**
* contact form handler
*/
component
{
// inject the ValidateThis plugin
property name="Validator" inject="coldbox:myplugin:ValidateThisCB3Plugin" scope="instance";
/**
* I list tasks
**/
void function index( required event )
{
var rc = arguments.event.getCollection();
// get all tasks
rc.tasks = EntityLoad( "Task" );
arguments.event.setView( "tasks/index" );
}
/**
* I add/edit tasks
**/
void function maintain( required event )
{
var rc = arguments.event.getCollection();
if ( StructKeyExists( rc, "taskID" ) )
{
// task exists so edit
rc.task = EntityLoadByPK( "Task", Val( rc.taskID ) );
}
else
{
// new task if Task not already in request collection
arguments.event.paramValue( "task", EntityNew( "Task" ) );
}
arguments.event.setView( "tasks/form" );
}
void function save( required event )
{
var rc = arguments.event.getCollection();
if ( Val( rc.taskID ) > 0 )
{
// task exists so edit
rc.task = EntityLoadByPK( "Task", Val( rc.taskID ) );
}
else
{
// new task
rc.task = EntityNew( "Task" );
}
populateModel( model=rc.task, exclude="taskid" );
// get ValidateThis to validate the task entity
rc.validationresult = instance.Validator.validate( rc.task );
// check for any errors
if ( rc.validationresult.hasErrors() )
{
// validation has failed so redirect preserving the task
flash.persistRC( 'task,validationresult' );
setNextEvent( "tasks.maintain" );
}
else
{
// no errors so save and redirect
transaction
{
EntitySave( rc.task );
}
flash.put( "notice", "Task saved" );
setNextEvent( "tasks" );
}
}
}
Now we need the views, here is the view to show the list of tasks:
<cfoutput>
<h1>Tasks list</h1>
<--- if a message exists, show it --->
<cfif flash.exists( "notice" )>
<p>#flash.get( "notice" )#</p>
</cfif>
<p><a href="#event.buildLink( "tasks.maintain" )#">Add task</a></p>
<ul>
<cfloop array="#rc.tasks#" index="task">
<li><a href="#event.buildLink( linkto="tasks.maintain", queryString="taskid=#task.getTaskID()#" )#">#task.getTitle()#</a></li>
</cfloop>
</ul>
</cfoutput>
Here is the form we use to add and edit task entities.
<cfoutput>
<h1>Maintain Task</h1>
<!--- display any validation errors --->
<cfif StructKeyExists( rc, "validationresult" ) AND rc.validationresult.hasErrors()>
<div style="border:2px solid red">
<p>Please review the following:</p>
<ul>
<cfloop array="#rc.validationresult.getFailureMessages()#" index="message">
<li>#message#</li>
</cfloop>
</ul>
</div>
</cfif>
<form action="#event.buildlink( 'tasks.save' )#" method="post" id="taskform">
<dl>
<dt><label for="title">Title: </label></dt>
<dd><input type="text" name="title" id="title" size="40" value="#HtmlEditFormat( rc.task.getTitle() )#" /></dd>
<dt><label for="comments">Comments: </label></dt>
<dd><textarea name="comments" id="comments" rows="5" cols="50">#HtmlEditFormat( rc.task.getComments() )#</textarea></dd>
</dl>
<input type="hidden" name="taskid" id="taskID" value="#rc.task.getTaskID()#" />
<input type="submit" value="submit" />
</form>
</cfoutput>
Finally we need to create the rules for what a Task needs to be considered valid. To do this we create an XML (you can use JSON if you prefer) file and put it in the same directory as the Task.cfc. ValidateThis will find your rules file by convention (which as a ColdBox user I like!). Here is my Task.xml.cfm file.
<?xml version="1.0" encoding="UTF-8"?>
<validateThis xsi:noNamespaceSchemaLocation="validateThis.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<objectProperties>
<property name="Title">
<rule type="required" />
<rule type="rangelength">
<param name="minlength" value="2" />
<param name="maxlength" value="40" />
</rule>
</property>
<property name="Comments">
<rule type="maxlength">
<param name="maxlength" value="200" />
</rule>
</property>
</objectProperties>
</validateThis>
That's it, we now have a working application with server side validation. But wait, ValidateThis can also generate client side validation using jQuery and the excellent Validation jQuery plugin. So let's add that to the task manager application.
The ValidateThis download includes jQuery and the Validation and Field plugins and can add these to your pages for you, but I like to have the latest version of jQuery so tend to add them manually by adding the following three lines in the layout file .
<script type="text/javascript" src="assets/js/jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="assets/js/jquery.validate.min.js"></script>
<script type="text/javascript" src="assets/js/jquery.field.min.js"></script>
By default, ValidateThis expects the form to have an id of "frmMain". If you don't like that naming convention, then you can override it globally, or on a page-by-page basis. To set it globally, (and whilst we're at it let's as tell ValidateThis not to worry about including the javascript libraries) then all you need to do is add the following to your Coldbox.cfc custom settings:
// custom settings
settings = {
ValidateThisConfig = {
defaultFormName='formToValidate',
JSIncludes=false
}
};
Instead of setting it globally, I'm going to set it on a per-page basis by passing the formName argument to the getValidationScript() method in my handler. So let's have a look at the updated maintain event in the tasks.cfc handler:
/**
* I add/edit tasks
**/
void function maintain( required event )
{
var rc = arguments.event.getCollection();
if ( StructKeyExists( rc, "taskID" ) )
{
// task exists so edit
rc.task = EntityLoadByPK( "Task", Val( rc.taskID ) );
}
else
{
// new task if Task not already in request collection
arguments.event.paramValue( "task", EntityNew( "Task" ) );
}
// get client side validation helper scripts, but don't include jQuery
$htmlhead( instance.Validator.getInitializationScript();
// get client side validation script
$htmlhead( instance.Validator.getValidationScript( theObject=rc.task, formName="taskform" ) );
arguments.event.setView( "tasks/form" );
}
I'm using the $htmlhead method (which is a ColdBox facade for cfhtmlhead) to put the JavaScript that ValidateThis generates for us in the <head>. As you can see I'm passing in the object I want to validate and also, then name of the form I want the JavaScript validation assigned to. As I mentioned above this is optional.
That's it, we now have client side and server side validation! Well, actually the client side validation isn't going to work at the moment as ValidateThis uses the same case as you specify in your rules file, and as JavaScript is case sensitive, it will be trying to find a field named "Title", whereas we've actually called it "title" in the HTML. So a quick update the XML rules and a reload of ColdBox later, it's all working. One final thing to note is that it's the field name, not the id which is used for client side validation.
- Posted in:
- ColdFusion
- Coldbox
- ValidateThis


Thanks!
Comment by Andy – December 16, 2010
The difference is that if you use the inheritance approach, coldbox HAS to be installed in the webroot or a global coldfusion mapping has to be defined. If you use the composition approach, then coldbox can be ANYWHERE and you can use a per application mapping. That's about it, in terms of functionality, the rest remains the same.
Also, the inheritance approach is a little cleaner than the composition approach, but the composition approach since it is more decoupled is more flexible.
Comment by Luis Majano – December 17, 2010
There's an interesting post and comment thread about this on Barney Boisvert's blog:
www.barneyb.com/barneyblog/2010/02/12/applicationcfc-extends-front-controller-is-evil/
Comment by John Whish – December 19, 2010
Comment by Vladimir Ugryumov – March 04, 2011
Comment by John Whish – March 07, 2011
Comment by Vladimir Ugryumov – March 09, 2011