ValidateThis SOTR2011 presentation code
I was lucky enough to speak at the Scotch on the Rocks conference in Edinburgh last week on ValidateThis. If you couldn't attend or for went to see Ray Camden or Darren Walker's talks instead (I would have liked to go to both!), here is the code samples I used. Obviously this code is intended to be shown as part of a presentation and I did demonstrate each example, however I've added some notes to hopeful explain what I'm showing.
The basics...
Loading and configuring ValidateThis
ValidateThis accepts a struct of values which can be used to configure it. All of these settings are optional. The following shows different ways to load ValidateThis.
<cfscript>
// ValidateThis config (all settings optional)
config = {
// Don't prefix failure messages with 'The'
defaultFailureMessagePrefix="Oi Mate! "
};
// load and configure ValidateThis
application.ValidateThis = new validateThis.ValidateThis( config );
WriteDump( var=application.ValidateThis, label="ValidateThis" );
WriteDump( var=application.ValidateThis.getValidateThisConfig(), label="ValidateThis Config" );
</cfscript>
ColdSpring bean definition:
<beans>
<bean id="ValidateThis" class="ValidateThis.ValidateThis">
<constructor-arg name="ValidateThisConfig">
<map>
<entry key="defaultFailureMessagePrefix"><value>Oi Mate! </value></entry>
</map>
</constructor-arg>
</bean>
</beans>
ColdBox interceptor:
// Configure ColdBox Application
function configure()
{
// ...coldbox configuration settings go here....
interceptors =
[
//ValidateThis
{
class="validatethis.extras.coldbox.ColdBoxValidateThisInterceptor",
properties =
{
// Don't prefix failure messages with 'The'
defaultFailureMessagePrefix="Oi Mate! "
}
}
];
}
Simple Validation Example
Consider the following, simple User object
/**
* I model a User
*/
component accessors="true"
{
property name="username"
}
As you can see there is no validation code in the object. Instead, validation rules are defined in an external file.
User.xml.cfm
<?xml version="1.0" encoding="UTF-8"?>
<validateThis>
<objectProperties>
<!-- username is required -->
<property name="username">
<rule type="required" />
</property>
</objectProperties>
</validateThis>
The above is a simple rule definition (written in XML), which tells ValidateThis that the username property is required.
For the XML averse, this can be written as JSON
{
"validateThis" : {
"objectProperties" : [{
"name":"username",
"rules": [{
"type":"required"
}]
}]}
}
Or even as annotations added to the User object
/**
* I model a User
*/
component accessors="true"
{
/**
* @vtRules [{"type":"required"}]
*/
property name="username";
}
So, how do we use ValidateThis in an application? The following is a simple example of a login form for a chat room:
<cfscript>
// load ValidateThis
application.ValidateThis = new validateThis.ValidateThis();
// get a new user object
User = new User();
if ( StructCount( form ) )
{
User.setUserName( form.username );
// validate the user object, VT will find the rules
ValidationResult = application.ValidateThis.validate( User );
// check for validation failures
if ( ValidationResult.hasErrors() )
{
WriteDump( ValidationResult.getFailureMessages() );
}
else
{
WriteOutput( "Welcome " & User.getUsername() );
}
}
</cfscript>
<cfoutput>
<p>Enter the Scotch on the Rocks chat room...</p>
<form action="#CGI.SCRIPT_NAME#" method="post">
<dl>
<dt><label for="username">Username (required)</label></dt>
<dd><input type="text" name="username" id="username" value="#HtmlEditFormat( User.getusername() )#" /></dd>
</dl>
<input type="submit" name="submit" id="submit" value="submit" />
</form>
</cfoutput>
There are a couple of things to look at in the example above. Before I explain what is happening, I should point out that the demo loads ValidateThis at the top of the script. This is simply for the demo. In your real application, you'd load ValidateThis once as a singleton (in Application.cfc or via a BeanFactory) and not on each request.
The two lines to look at are 11 and 13. On line 11, I'm asking ValidateThis to validate the passed in User object. By default ValidateThis will look for a rule definition file in the same directory as the Object, named User.xml.cfm or User.json.cfm (the .cfm extention is optional but recommended). The validate method returns a Validation Result object. On line 13, I'm asking the result object if it has any errors. If there are errors, then I can get an array of them on by calling getFailureMessages().
Validating against a struct
ValidateThis can be used to validate a simple struct (for example the form scope). The code is similar to the previous example where I'm validating an object with some small changes. Here's the code
<cfscript>
// set definitionPath (can be a list)
config = { definitionPath="/sotr2011/example1/" };
application.ValidateThis = new validateThis.ValidateThis( config );
if ( StructCount( form ) )
{
// pass form scope and object type
ValidationResult = application.ValidateThis.validate( objectType="User", theObject=form );
if ( ValidationResult.hasErrors() )
{
WriteDump( ValidationResult.getFailureMessages() );
}
else
{
WriteOutput( "Welcome " & User.getUsername() );
}
}
else
{
form.username = "";
}
</cfscript>
The first change is on line 3 where you'll note that I'm setting a key called definitionPath. This key is used to set where rule definition rules are stored. You can use a comma delimited list to specify multiple directories. You can use this key when validating objects if you don't like the idea of having you rule files in the same directory as you class file.
The second thing that has changed is that we are passing in the objectType and theObject arguments to the validate method. This is simply so that ValidateThis knows which rule definition file to use. Other than that the code is the same.
Rules
So far we've done a very simple example of making a property required. Here is a more complex rule definition file.
<?xml version="1.0" encoding="UTF-8"?>
<validateThis>
<objectProperties>
<property name="username">
<rule type="required" failureMessage="Dude, where's the username?" />
<rule type="rangelength">
<param name="minlength" value="5" />
<param name="maxlength" value="30" />
</rule>
</property>
<property name="email" desc="Email address">
<rule type="required" />
<rule type="email" failureMessage="Hey, that ain't no email!" />
</property>
<property name="password">
<rule type="required" />
<rule type="regex" failureMessage="I wanna see letters and numbers buddy.">
<param name="regex" value="(([A-Za-z]{1,}[0-9]{1,})|([0-9]{1,}[A-Za-z]{1,}))" />
</rule>
</property>
<property name="passwordconfirmation" desc="Verify Password">
<rule type="required" />
<rule type="equalTo">
<param name="ComparePropertyName" value="password" />
</rule>
</property>
</objectProperties>
</validateThis>
On my presentation I did talk about what each rule type did and shown an example of it in action, but I'm too lazy to type it here so I suggest you have a look at ValidateThis docs. I will however point out a few interesting things; firstly you might notice that on some rules I'm setting an failureMessage attribute. I use this when I want to override the default messages that ValidateThis generates. The fact that ValidateThis will generate messages for you is a nice time saver, and you can always define your own if you want to.
Contexts
This is a really powerful feature of ValidateThis. Imagine that you have a user object. A user object gets used in different contexts depending on what you're doing. For example a login form only has two fields; username and password. However when you want a user to sign up, then you want there username, password, password confirmation and maybe something like email address as well. This is where contexts come into play.
In your rules definition file for the user you simply set up contexts and then set which rules apply to which contexts. There can be one or more contexts. If you don't specify a context on the rule, then it applies to all. Here is the sample rule definition I used.
<?xml version="1.0" encoding="UTF-8"?>
<validateThis>
<objectProperties>
<property name="username">
<rule type="required" failureMessage="Dude, where's the username?" />
<rule type="rangelength" contexts="register">
<param name="minlength" value="5" />
<param name="maxlength" value="30" />
</rule>
<!-- custom method -->
<rule type="custom" contexts="register" failureMessage="Sorry, that username is already taken. Please try again.">
<param name="methodname" value="isUniqueUsername" />
</rule>
</property>
<property name="email" desc="Email address">
<rule type="required" contexts="register" />
<rule type="email" contexts="register" failureMessage="Hey, that ain't no email!" />
</property>
<property name="password">
<rule type="required" />
<rule type="regex" contexts="register" failureMessage="I wanna see letters and numbers buddy.">
<param name="regex" value="(([A-Za-z]{1,}[0-9]{1,})|([0-9]{1,}[A-Za-z]{1,}))" />
</rule>
<!-- check for silly password -->
<rule type="inList" contexts="register" value="god,letmein,opensesame" failureMessage="Try to pick an original password!" />
<!-- this is a dynamic expression -->
<rule type="expression" contexts="register" failureMessage="The password should not be your username!">
<param name="expression" value="getPassword() neq getUsername()" />
</rule>
<rule type="expression" contexts="register" failureMessage="The password should not be your email address!">
<param name="expression" value="getPassword() neq getEmail()" />
</rule>
</property>
<property name="passwordconfirmation" desc="Verify Password">
<rule type="required" contexts="register" />
<!-- compare two properties -->
<rule type="equalTo" contexts="register">
<param name="ComparePropertyName" value="password" />
</rule>
</property>
</objectProperties>
</validateThis>
As you can see I have added a contexts attribute to the rules.
Here's some code showing the register context in action:
<cfscript>
// load ValidateThis
ValidateThis = new validateThis.ValidateThis();
User = new User();
if ( StructCount( form ) )
{
User.setUserName( form.username );
User.setEmail( form.email );
User.setPassword( form.password );
User.setPasswordConfirmation( form.passwordconfirmation );
// validate the User for the register context
ValidationResult = ValidateThis.validate( theObject=User, context="register" );
if ( ValidationResult.hasErrors() )
{
WriteDump( ValidationResult.getFailureMessages() );
}
else
{
WriteOutput( "Welcome " & User.getUsername() );
}
}
</cfscript>
<cfoutput>
<p>Register for the Scotch on the Rocks chat room...</p>
<form action="#CGI.SCRIPT_NAME#" method="post">
<dl>
<dt><label for="username">Username (required)</label></dt>
<dd><input type="text" name="username" id="username" value="#HtmlEditFormat( User.getusername() )#" /></dd>
<dt><label for="email">Email (required)</label></dt>
<dd><input type="text" name="email" id="email" value="#HtmlEditFormat( User.getemail() )#" /></dd>
<dt><label for="password">Password (required)</label></dt>
<dd><input type="text" name="password" id="password" value="#HtmlEditFormat( User.getpassword() )#" /></dd>
<dt><label for="passwordconfirmation">Confirm Password (required)</label></dt>
<dd><input type="text" name="passwordconfirmation" id="passwordconfirmation" value="#HtmlEditFormat( User.getpasswordconfirmation() )#" /></dd>
</dl>
<input type="submit" name="submit" id="submit" value="submit" />
</form>
</cfoutput>
Using exactly the same rules definition file for the login context
<cfscript>
// load ValidateThis
ValidateThis = new validateThis.ValidateThis();
User = new User();
if ( StructCount( form ) )
{
User.setUserName( form.username );
User.setPassword( form.password );
// validate the User for the login context
ValidationResult = ValidateThis.validate( theObject=User, context="login" );
if ( ValidationResult.hasErrors() )
{
WriteDump( ValidationResult.getFailureMessages() );
}
else
{
WriteOutput( "Welcome " & User.getUsername() );
}
}
</cfscript>
<cfoutput>
<p>Login to the Scotch on the Rocks Reach Around chat room...</p>
<form action="#CGI.SCRIPT_NAME#" method="post">
<dl>
<dt><label for="username">Username (required)</label></dt>
<dd><input type="text" name="username" id="username" value="#HtmlEditFormat( User.getusername() )#" /></dd>
<dt><label for="password">Password (required)</label></dt>
<dd><input type="text" name="password" id="password" value="#HtmlEditFormat( User.getpassword() )#" /></dd>
</dl>
<input type="submit" name="submit" id="submit" value="submit" />
</form>
</cfoutput>
Client side validation
So far all the validation has been done server-side, but ValidateThis will generate client side Javascript validation for you from the same set of rules. This is great, because we don't have to worry about updating client side validation when server side validation changes.
ValidateThis uses a CDN network to load the required Javascript scripts; namely jQuery 1.5 and the jQuery validation plugin. As I was doing my presentation at a conference with un-reliable Wi-Fi, I've opted to include the Javascript libraries myself. You may want to do this if you want to use an older version of jQuery, or you already load jQuery in all your pages.
<cfscript>
// configure & load ValidateThis
config = {
// tell ValidateThis that we'll include JS ourself
JSIncludes = false,
// tell ValidateThis what the form name is (frmMain by default)
defaultFormName = "validateme"
};
ValidateThis = new validateThis.ValidateThis( config );
User = new User();
if ( StructCount( form ) )
{
User.setUserName( form.username );
User.setEmail( form.email );
User.setPassword( form.password );
User.setPasswordConfirmation( form.passwordconfirmation );
User.setStatus( form.status );
User.setStatusOther( form.statusother );
// validate the User for the register context
ValidationResult = ValidateThis.validate( theObject=User, context="register" );
if ( ValidationResult.hasErrors() )
{
WriteDump( ValidationResult.getFailureMessages() );
}
else
{
WriteOutput( "Welcome " & User.getUsername() );
}
}
</cfscript>
<cfoutput>
<p>Register for the Scotch on the Rocks chat room...</p>
<form action="#CGI.SCRIPT_NAME#" method="post" id="validateme">
<dl>
<dt><label for="username">Username (required)</label></dt>
<dd><input type="text" name="username" id="username" value="#HtmlEditFormat( User.getusername() )#" /></dd>
<dt><label for="email">Email (required)</label></dt>
<dd><input type="text" name="email" id="email" value="#HtmlEditFormat( User.getemail() )#" /></dd>
<dt><label for="password">Password (required)</label></dt>
<dd><input type="text" name="password" id="password" value="#HtmlEditFormat( User.getpassword() )#" /></dd>
<dt><label for="passwordconfirmation">Confirm Password (required)</label></dt>
<dd><input type="text" name="passwordconfirmation" id="passwordconfirmation" value="#HtmlEditFormat( User.getpasswordconfirmation() )#" /></dd>
<dt><label for="status">Status (required)</label></dt>
<dd><select name="status" id="status">
<option value="">Please choose</option>
<option value="1">Bored</option>
<option value="2">Fruity</option>
<option value="0">Other (please state)</option>
</select></dd>
<dt><label for="statusother">Other Status</label></dt>
<dd><input type="text" name="statusother" id="statusother" value="#HtmlEditFormat( User.getstatusother() )#" /></dd>
</dl>
<input type="submit" name="submit" id="submit" value="submit" />
</form>
<!--- include javascript validation libraries --->
<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>
<!--- load general client side validation scripts --->
#ValidateThis.getInitializationScript()#
<!---load client side validation specific to the User and context --->
#ValidateThis.getValidationScript( theObject=User, context="register" )#
</cfoutput>
To use the client side validation, you'll notice that I have included the jQuery and jQuery validate plugin inline at the bottom of example, as I stated above, you can ue the CDN versions if you prefer. If you do decided to include the Javascript libraries yourself, then you'll need to set the JSIncludes config setting to false to tell ValidateThis not to write out the Javascript. I have also set the defaultName setting in my config. This is the id of the form you want to bind client side validation to. I don't tend to do this, instead I pass the formName argument to getValidationScript() so that I can set it on a page-by-page basis.
The ValidateThis.getInitializationScript() line simply tells ValidateThis to include all the generic Javascript validation code. The code to load the specific rules for the User object in the register context is on line 74. The context is only required if you are using contexts.
Conditions
When you have a complex validation to perform which may depend on lots of factors which can't be replicated using the rules, then you can use conditions. In the example below, I have added a conditions node, where I have created a NeedTelephone condition. If you look at the telephone property, you'll see that it references the NeedTelephone condition.
The serverTest attribute of the condition is where you enter any ColdFusion (server side) validation code. The clientTest attribute value is the client side Javascript; you will need to make sure your Javascript is encoded to be XML safe. If you aren't the best Javascript developer on the planet, don't worry, the clientTest attribute is optional.
<?xml version="1.0" encoding="UTF-8"?>
<validateThis>
<conditions>
<condition name="NeedTelephone"
serverTest="getTextMe() OR getCallMe()"
clientTest="$("[name='textme']").is(':checked') || $("[name='callme']").is(':checked');" />
</conditions>
<objectProperties>
<property name="name">
<rule type="required" failureMessage="What's your name?" />
</property>
<property name="email" desc="Email address">
<rule type="required" />
<rule type="email" failureMessage="Hey, that ain't no email!" />
</property>
<property name="telephone">
<rule type="required" condition="NeedTelephone" />
</property>
</objectProperties>
</validateThis>
Helper Methods
ValidateThis has a whole bunch of methods which I'm not going to go into here, but I suggest you check out the API. Here is a example of the getRequiredFields() method, which returns a struct of all the properties that are required for the given object and context. I'm also using the getFailureMessagesByField() method to return an struct containing an array of error messages.
<cfscript>
// configure & load ValidateThis
ValidateThis = new validateThis.ValidateThis();
User = new User();
// by default we have no failures
failureMessages= {};
if ( StructCount( form ) )
{
User.setUserName( form.username );
User.setPassword( form.password );
// validate the User for the login context
ValidationResult = ValidateThis.validate( theObject=User, context="login" );
if ( ValidationResult.hasErrors() )
{
failureMessages = ValidationResult.getFailureMessagesByField();
}
else
{
WriteOutput( "Welcome " & User.getUsername() );
}
}
// get required fields for the login context
requiredFields = ValidateThis.getRequiredFields( theObject=User, context="login" );
</cfscript>
<cfoutput>
<p>Login to the Fuzzy Orange Reach Around chat room...</p>
<form action="#CGI.SCRIPT_NAME#" method="post" id="validateme">
<dl>
<dt><label for="username">Username <cfif StructKeyExists( requiredFields, "username" )>(required)</cfif></label></dt>
<dd>
<input type="text" name="username" id="username" value="#HtmlEditFormat( User.getusername() )#" />
<cfif StructKeyExists( failureMessages, "username" )>
<cfloop array="#failureMessages.username#" index="msg">
<br />#msg#
</cfloop>
</cfif>
</dd>
<dt><label for="password">Password <cfif StructKeyExists( requiredFields, "password" )>(required)</cfif></label></dt>
<dd>
<input type="text" name="password" id="password" value="#HtmlEditFormat( User.getpassword() )#" />
<cfif StructKeyExists( failureMessages, "password" )>
<cfloop array="#failureMessages.password#" index="msg">
<br />#msg#
</cfloop>
</cfif>
</dd>
<dt><label for="remember">Remember Me? <cfif StructKeyExists( requiredFields, "remember" )>(required)</cfif></label></dt>
<dd><input type="checkbox" name="remember" id="remember" value="1" /></dd>
</dl>
<input type="submit" name="submit" id="submit" value="submit" />
</form>
<!--- not using client side validation in this demo to show failureMessages --->
</cfoutput>
In version 0.99 (current at the time of presenting), two new methods have been introduced called propertyIsRequired() and fieldIsRequired() which can be used instead of getRequiredFields().
What next?
- Posted in:
- ColdFusion
- SOTR
- ValidateThis


One thing: It isn't necessary to use the contexts/context elements at the top of the xml file in order to make use of contexts in your rules. The only reason those elements exist is to allow you to associate a form name with a context. If you're not doing that (and you're not in your example), then you can leave them out entirely.
Comment by Bob Silverberg – March 09, 2011
Comment by John Whish – March 10, 2011
@ Flatware - Thanks for that tip on contexts. I should have known that was the case, given some of the other features, but I just followed the examples and included it. I should have tried leaving that block out. Heh.
Comment by Matt Quackenbush – March 16, 2011