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.
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
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
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.
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.
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
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.





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.