My OO form validation
December 05, 2008
One of the most common tasks that you'll do when building web applications is capturing data from a form. This is fairly straight forward (although rather tedious task) if we go down the procedural root, but how do we do it when we want to be Object Orientated? Well, this is my approach.
First let's look at a simple form to sign up to an email mailing list. With procedural code, subscribe.cfm would look something like this (for simplicity I've put it all in the same script):
<cfif StructKeyExists( form, "fieldnames" )>
<!--- form submitted --->
<cfif Len( form.email ) eq 0>
<cfset message = "Please enter your email address." />
<cfelseif NOT IsValid( 'email', form.email )>
<cfset message = "Please check your email address." />
<cfelse>
<!--- all good so do something and redirect --->
</cfif>
<cfelse>
<!--- set up default values --->
<cfset form.name = "" />
<cfset form.email = "" />
</cfif>
<cfoutput>
<html>
<body>
<cfif IsDefined( "message" )>
<p>#message#</p>
</cfif>
<form action="subscribe.cfm" method="post">
<div class="formrow">
<label for="name">Name</label>
<input name="name" id="name" value="#form.name#" type="text" />
</div>
<div class="formrow">
<label for="email">Email</label>
<input name="email" id="email" value="#form.email#" type="text" />
<em>required</em>
</div>
<div class="formrow">
<input name="submit" id="submit" value="Subscribe Me!" type="submit" />
</div>
</form>
</body>
</html>
</cfoutput>
Now let's try it OO style!
One of the benefits of OO is that you (should) end up with lots of reusable code. So to start here is my generic validator class.
validator.cfc
<cfcomponent output="false"
hint="I am an abstract class do not instantiate me directly!">
<cffunction name="init"
access="package"
output="false"
hint="I am the constructor">
<cfset var index = "" />
<cfset variables.properties = GetMetaData( this ).properties />
<cfset variables.fields = StructNew() />
<cfset variables.errors = StructNew() />
<!--- set up all the expected form fields and set default values --->
<cfloop from="1" to="#ArrayLen( properties )#" index="index">
<cfif StructKeyExists( properties[index], "default" )>
<cfset variables.fields[ properties[index].name ] = properties[index].default />
<cfelse>
<cfset variables.fields[ properties[index].name ] = "" />
</cfif>
</cfloop>
<cfreturn this />
</cffunction>
<!--- manipulate single fields --->
<cffunction name="setField"
access="public"
output="false"
returntype="boolean"
hint="returns true if the field exists">
<cfargument name="fieldname"
required="true" />
<cfargument name="fieldvalue"
required="true" />
<cfset var exists = StructKeyExists( variables.fields, arguments.fieldname ) />
<cfset variables.fields[ arguments.fieldname ] = arguments.fieldvalue />
<cfreturn exists />
</cffunction>
<!--- manipulate multiple fields --->
<cffunction name="getFields"
access="public"
output="false"
returntype="struct">
<cfreturn variables.fields />
</cffunction>
<cffunction name="setFields"
access="public"
output="false"
returntype="void">
<cfargument name="fieldpair"
required="true"
type="struct" />
<cfset var item = "" />
<cfloop collection="#arguments.fieldpair#" item="item">
<cfif StructKeyExists( variables.instance.fields, item )>
<cfset StructUpdate( variables.instance.fields, item, arguments.fieldpair[ item ] ) />
</cfif>
</cfloop>
</cffunction>
<!--- other public methods --->
<cffunction name="validate"
access="public"
output="false"
returntype="void">
<cfset var index = "" />
<cfloop from="1" to="#ArrayLen( variables.properties )#" index="index">
<cfif variables.properties[index].required
AND Len( variables.fields[ variables.properties[index].name ] ) eq 0>
<!--- field is required but blank --->
<cfif StructKeyExists( variables.properties[index], "requiredfail" )>
<cfset setError( variables.properties[index].name, variables.properties[index].requiredfail ) />
<cfelse>
<cfset setError( variables.properties[index].name, variables.properties[index].name & " is required." ) />
</cfif>
<cfelseif StructKeyExists( variables.properties[index], "type" )
AND NOT IsValid( variables.properties[index].type, variables.fields[ variables.properties[index].name ] )>
<!--- field is not blank but has an invalid datatype --->
<cfif StructKeyExists( variables.properties[index], "typefail" )>
<cfset setError( variables.properties[index].name, variables.properties[index].typefail ) />
<cfelse>
<cfset setError( variables.properties[index].name, variables.fields[ variables.properties[index].name ] & " is not valid" ) />
</cfif>
</cfif>
</cfloop>
</cffunction>
<cffunction name="hasErrors"
access="public"
output="false"
returntype="boolean">
<cfreturn StructCount(variables.errors) gt 0 />
</cffunction>
<cffunction name="getErrors"
access="public"
output="false"
returntype="struct">
<cfreturn variables.errors />
</cffunction>
<!--- private methods --->
<cffunction name="setError"
access="private"
output="false"
returntype="void">
<cfargument name="field"
required="true" />
<cfargument name="message"
required="true" />
<cfset variables.errors[arguments.field] = arguments.message />
</cffunction>
</cfcomponent>
Now, that looks like a lot of code to reproduce our procedural example (and we haven't finished yet!) but it is important to note that this class is generic and is reusable for all validation. So how do we use it?
I'm a big fan of using meta data so my validation class uses it to define the fields, type, messages and if they are required. All I need to do is create a new class which is specific to my subscription form.
SubscribeFormHelper.cfc.
<cfcomponent extends="validator"
output="false">
<!---
----------------------------------------------------------------------
Define expected form fields as properties
----------------------------------------------------------------------
Required attributes:
* name
* required [true|false]
Optional attributes
* requiredfail - message if required field is blank.
* default - sets the initial form value
* type - must match against the type parameter of the IsValid function
* typefail - message if values is an invalid type
----------------------------------------------------------------------
--->
<cfproperty name="name"
required="false" />
<cfproperty name="email"
required="true"
requiredfail="Please enter your email."
type="email"
typefail="Please check your email address." />
<cffunction name="init"
access="public"
output="false"
hint="I am the constructor">
<cfset super.init() />
<cfreturn this />
</cffunction>
</cfcomponent>
Simple huh! As you can see it extends the generic validator class we created (so make sure it is the same directory), which reads the properties we have declared.
Now that we've got out generic validator class and also a specific class for our form. We can wire it up to our form (again I've put it all in the same script for clarity).
<cfif StructKeyExists( form, "fieldnames" )>
<!--- form submitted --->
<!--- Initiate our SubscribeFormHelper object --->
<cfset formhelper = CreateObject("component", "SubscribeFormHelper").init() />
<!--- Pass the form data to our object --->
<cfset formhelper.setFields( form ) />
<!--- Validate the form data --->
<cfset formhelper.validate() />
<!--- Check for errors --->
<cfif NOT formhelper.hasErrors()>
<!--- all good so do something and redirect --->
<cfelse>
<!--- We have errors --->
<cfset errors = formhelper.getErrors() />
<cfset data = formhelper.getFields() />
</cfif>
<cfelse>
<!--- Initiate our SubscribeFormHelper object --->
<cfset formhelper = CreateObject("component", "SubscribeFormHelper").init() />
<cfset data = formhelper.getFields() />
</cfif>
<html>
<body>
<cfif IsDefined( "errors" )>
<ul>
<cfloop collection="#errors#" item="item">
<li>#StructFind(errors, item)#</li>
</cfloop>
</ul>
</cfif>
<form action="subscribe.cfm" method="post">
<div class="formrow">
<label for="name">Name</label>
<input name="name" id="name" value="#data.name#" type="text" />
</div>
<div class="formrow">
<label for="email">Email</label>
<input name="email" id="email" value="#data.email#" type="text" />
<em>required</em>
</div>
<div class="formrow">
<input name="submit" id="submit" value="Subscribe Me!" type="submit" />
</div>
</form>
</body>
</html>
So there you have it, this is pretty much how I do form validation. I hope that has been interesting (and correct without too many typos!).
I've just had a quick look on RIAForge before posting this and can see 4 projects listed that look like they are OO validation frameworks. I admit that I haven't looked at them, as I like to do it the long way so that I understand everything, but they are probably well worth investigating (and I really should to!).
- Posted in:
- ColdFusion
- OOP
5 comments
Leave a comment
If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)

Comment by Nic – December 05, 2008
Comment by Ben Nadel – December 05, 2008
I'm sure that the ones on RIAForge are much more "fully featured", but I thought I'd post mine to hopefully help others understand one way of doing OO style form validation.
Comment by John Whish – December 08, 2008
Comment by Akbarsait – December 13, 2008
Comment by John Whish – December 13, 2008