Home | Blog | Twitter @AndyJ | Contact Me | Snippets/Downloads | RSS

Model Glue Form Validation

I've been working on a app with a couple of forms and wanted to break the validation rules into different group of rules e.g.

1. Rules for address
2. Rules for personal information
3. Rules specific per form.

What this allows me to do is is per validation call in the controller just use the validators i need.

How I am doing it:

1. Create a controller called validator.cfc
2. Create you validation.cfc's i.e.
  1. validator_Personal.cfc
  2. validator_address.cfc
  3. validator_other.cfc
Your validation cfc's should look like this:
   view plainprintabout
 <cfcomponent>
     <cffunction name="validate" returnType="ModelGlue.Util.ValidationErrorCollection">
         <cfargument name="formFields">
 
         <cfset var val = createObject("component", "ModelGlue.Util.ValidationErrorCollection").init() />
 
         <cfif structKeyExists(formFields, "address1") AND not len(trim(formFields.address1))>
             <cfset val.addError("address1", "Please enter the first line of your address.") />
         </cfif>
10      </cffunction>
11  </cfcomponent>


3. In validator controller create a listener which has calls the validator objects e.g.

   view plainprintabout
 <cffunction name="validateDetails" access="Public" returnType="ModelGlue.Core.Event"
  output="false" hint="I am an event handler.">

  <cfargument name="event" type="ModelGlue.Core.Event" required="true">
         
     <!--- These are added here for clarity. Usually they should go in the init() --->
     <cfset variables.validatePersonal = createObject("component", "model.validator_Personal") >
     <cfset variables.validateAddress = createObject("component", "model.validator_address")>
     
     <!--- Validate the values --->
10      <cfset valReq = variables.validatePersonal.validate(arguments.event.getAllValues()) />
11      <cfset valAddress = variables.validateAddress.validate(arguments.event.getAllValues()) />        
12      
13      <!--- here we bring to the sets of rules together into on value--->
14      <cfset valReq.merge(valAddress) />
15      
16      <!--- Now we set the result --->
17      <cfif valReq.hasErrors()>
18          <cfset arguments.event.addResult("inValid") />    
19          <cfset arguments.event.setValue("validation", valReq.getErrors()) />
20      <cfelse>
21          <cfset arguments.event.addResult("valid") />
22      </cfif>
23  
24   <cfreturn arguments.event />
25  </cffunction>


4. To finish it off, in your form you need the usual viewState calls

   view plainprintabout
 <!--- Gets any validation errors --->
 <cfset val = viewState.getValue("validation", structNew()) />
TweetBacks
Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Aaron's Gravatar I know this is a very old blog posting but I came across it while trying to diagnose a problem that is resulting from me taking a similar approach to validation of data. I am curious how do you end up displaying the validation errors for the user?

I had been doing this in my ModelGlue.xml :
<results>
   <result name="ValidationError" do="registrationForm" redirect="false" />
   <result do="view.template" />
</results>

My problem stems from that redirect being false though. I noticed that when I do that, my main template gets queued more than once in the MG debug information. This is causing my CFMenuItems to get some minor duplication. If I set the redirect to true then the page renders 100% correctly but then I no longer have access to the validation structure.

Interesting enough my ViewState.getValue('LASTNAME') still returns what I typed in with the return type set to true or false.
# Posted By Aaron | 11/3/08 8:37 PM
Andy Jarrett's Gravatar @Aaron

This is how I do my validation now. Basically I have the form loop back on to its own event. At that point I run the validation rules. If they fail then I redisplay the view, otherwise I redirect them to the next page.

<!-- Have the form loop back on to this event -->
<event-handler name="DisplayCustomerDetailsEvents">
   <broadcasts>
      <!-- This only runs when the form has been submitted -->
      <message name="validateCustomerDetails" />
   </broadcasts>         
   <results>
      <result do="view.template" />
      <!-- If validateCustomerDetails() is successful then move on -->
      <result name="success" do="successful" redirect="true" />                        
   </results>
   <views>            
      <include name="body" template="direct/dspCustomerdetails.cfm" />
   </views>
</event-handler>

Does this help?
# Posted By Andy Jarrett | 11/4/08 6:08 AM
Aaron's Gravatar I was doing something similar to that, what I noticed last night is it was the duel result elements that were causing the issue. What I did to get around that is when I had <result do="view.template" /> I added the name="success" to that. Seemed to have fixed my issue although I might play with it later today and see about changing them to something more inline with what you have done.
# Posted By Aaron | 11/4/08 8:53 AM
BlogCFC / created by Raymond Camden / running version 5.9.5.003 / Contact AndyJarrett.com / Pet Rescue SOS www.redgiraffes.co.uk