Learning Coldbox: Validating

I have tried many ways to validate data in ColdBox, and there are many ways! I have used 3rd party plug-ins and followed many blogs posts on the subject. I want to share one of my preferred ways to validate data in ColdBox. Its not the most polished method and it has its flaws (persisting values) but it has worked well for me!

First I will create an example handler (show below). This handler is used to display School data based on the ID and School URL passed to it. I won't go into the Application or the model here.

view plain print about
1<cffunction name="default" returntype="void" output="false">
2 <cfargument name="Event" required="true"/>
3 <cfscript>
4 var rc = event.getCollection();//RC Reference
5
var schoolBean = variables.surveyService.createSchoolBean(); //Create School Bean
6

7 rc.schoolBean = schoolBean;
8 event.setView("web/surveyHome"); // Set the View To Display, After Logic
9

10
</cfscript>
11</cffunction>

At the moment all this handler does is create my school bean ready to hold my data, and displays the results in a view. Now before I populate the bean with data I should validate the data being passed to the handler, right?

I do this by adding the following code to my handler

view plain print about
1//Validate Data Before Populating It In The Bean
2 if (variables.surveyService.validatePreBean(theScope=rc,theBean=schoolBean)){
3 rc.errorMsg = variables.surveyService.getErrorResults();//Get Errors To Display
4 }

So what does this do? Well it's crude at the moment but it runs a function in my service called validatePreBean (shown below). This function takes the arguments of the current requests and the bean with the validation function in it

view plain print about
1<!--- Service--->
2<!--- Validate Bean --->
3<cffunction name="validatePreBean" access="public" returntype="any" output="false">
4 <cfargument name="theScope" type="any" required="true" />
5 <cfargument name="theBean" type="any" required="true" />
6 <cfscript>
7 var results = "";
8 var errors = "";
9 results = arguments.theBean.validate(theScope);
10 if ArrayIsEmptyy(results)){// Array Is Empty No Errors Found
11
errors = false;//Set Flag For 'No' Errors
12
}
13 else {//Array Is Not Empty We Have Errors
14
errors = true; //Set Flag For Errors
15
    setErrorResults (results);
16 }
17 return errors;
18
</cfscript>
19</cffunction>
20<!--- Set Error Message --->
21<cffunction name="setErrorResults" access="public" returntype="void" output="false">
22 <cfargument name="errorResults" type="any" required="true" />
23 <cfset variables.instance.errorResults = arguments.errorResults />
24</cffunction>
25<!--- Get Errors Message --->
26<cffunction name="getErrorResults" access="public" returntype="any" output="false">
27 <cfreturn variables.instance.errorResults />
28</cffunction>

This function returns a boolean value. This value can be used back in my handler to check that we don't have errors before populating a bean and/or adding values to say a database. You will notice that I call a function within the service called setErrorResults(). My object shown below returns these results based on the arguments passed.

view plain print about
1<!--- My School Object--->
2    <cffunction name="validate" access="public" returntype="array" output="false">
3     <cfargument name="formData" type="struct" required="true" />
4        
5        <cfset var errors = arrayNew(1) />
6        <cfset var thisError = structNew() />
7        
8        
9        <!--- sch_id --->
10 <cfif NOT StructKeyExists(formData,"sch_id") OR NOT Len(formData.sch_id)>
11 <cfset thisError.field = "sch_id" />
12            <cfset thisError.type = "invalidType" />
13            <cfset thisError.message = "sch_id is required" />
14            <cfset arrayAppend(errors,duplicate(thisError)) />
15        <cfelse>
16        <!--- sch_id --->
17        <cfif (len(trim(formData.sch_id)) AND NOT isNumeric(trim(formData.sch_id)))>
18            <cfset thisError.field = "sch_id" />
19            <cfset thisError.type = "invalidType" />
20            <cfset thisError.message = "School ID is not numeric" />
21            <cfset arrayAppend(errors,duplicate(thisError)) />
22        </cfif>
23 </cfif>
24        
25    <!--- sch_name --->
26    <cfif StructKeyExists(formData,"sch_name")>
27        <cfif (NOT len(trim(formData.sch_name)))>
28            <cfset thisError.field = "sch_name" />
29            <cfset thisError.type = "required" />
30            <cfset thisError.message = "School Name is required" />
31            <cfset arrayAppend(errors,duplicate(thisError)) />
32        </cfif>
33        <cfif (len(trim(formData.sch_name)) AND NOT IsSimpleValue(trim(formData.sch_name)))>
34            <cfset thisError.field = "sch_name" />
35            <cfset thisError.type = "invalidType" />
36            <cfset thisError.message = "School is not a string" />
37            <cfset arrayAppend(errors,duplicate(thisError)) />
38        </cfif>
39        <cfif (len(trim(formData.sch_name)) GT 80)>
40            <cfset thisError.field = "sch_name" />
41            <cfset thisError.type = "tooLong" />
42            <cfset thisError.message = "School is too long" />
43            <cfset arrayAppend(errors,duplicate(thisError)) />
44        </cfif>
45    </cfif>
46    
47        <cfreturn errors />
48    </cffunction>

So now my handler knows if it has valid data, if not it also knows what data is not valid! Next we need to display any errors in the a view or even a layout.

view plain print about
1<cfif StructKeyExists(rc,"errorMsg")>
2    <div align="left" style="margin-top:10px;border:1px solid red;background:##fffff0;padding:10px">
3     The following problems were found...
4     <ul>
5     <cfloop index="ThisError" from="1" to="#ArrayLen(rc.errorMsg)#">
6 <li><cfoutput>#rc.errorMsg[ThisError].message#</cfoutput></li>
7 </cfloop>
8     </ul>
9    </div>
10</cfif>

Now, I could simplify the data coming back and use the message box plug-in, but I don't in this example

That's it, now back in my handler

view plain print about
1var rc = event.getCollection();//RC Reference
2 //Create School Bean
3 var schoolBean = variables.surveyService.createSchoolBean();
4 //Validate Data Before Populating It In The Bean
5 if (variables.surveyService.validatePreBean(theScope=rc,theBean=schoolBean)){
6 rc.errorMsg = variables.surveyService.getErrorResults();//Get Errors To Display
7 }
8 else {
9 //populate Bean With Data
10 getPlugin('beanFactory').populateBean(schoolBean);
11     variables.surveyService.getSchool(schoolBean);
12 }

Done. Persisting the data when there is an error is the only issue without repopulating the bean.

TweetBacks
Comments
TJ Downes's Gravatar Great post on the built-in ColdBox validation Glyn!

You might want to check out ValidateThis (www.validatethis.org). Bob has done a superb job of making validation simple and reusable, for both client and server-side. I use it in combination with cfUniform for my ColdBx apps and its saved me a ton of time and is a very elegant solution.
# Posted By TJ Downes | 21/02/10 16:30
Glyn Jackson's Gravatar It's something I have been meaning to checkout for some time, will do.
# Posted By Glyn Jackson | 24/02/10 22:25
 

About Me

Glyn Jackson, 28 years old, MD and senior developer of a development firm based in Staffordshire called Newebia Ltd. Academic background in BSc Information System & Internet Commerce. Online marketing expert (EE Ranked) and .NET developer. Has been developing with ColdFusion for 5 years and loves it. "I am not a veteran in ColdFusion but I do work on challenging projects which help me learn more about ColdFusion and if I can contribute to the community in anyway then, it's all good!"

Recommends

  • ColdFusion