Photo from Chile

Getting Started with VT and Transfer - Part III - Integrating VT Into Your Business Object

Update: The information in this post is no longer correct due to changes to the framework. A new series is available via the Getting Started with VT category of my blog.

Original content of the article follows:

In the next few posts in my series about getting started with Transfer and ValidateThis!, my validation framework for ColdFusion objects, we're going to take the simple sample application that was described in the previous post and add some server-side validations to it, using VT of course. That will involve two steps:

  • Integrate ValidateThis! into our Business Object.
  • Use the framework (via our Business Object) to perform server-side validations and make use of the results returned by the framework.

This post will cover the first step, integrating the framework into our Business Object. To do that we're going to have to make the following changes to or sample app:

  • Import the bean definitions for ValidateThis! into our Coldspring config.
  • Add a ValidateThisConfig bean to our Coldspring config.
  • Add a TDOBeanInjectorObserver bean to our Coldspring config.
  • Add a decorator for our User object.

That may sound like a lot of work, but it mostly involves copying boilerplate into existing files. Let's get started by importing the bean definitions for ValidateThis! into our Coldspring config. We're going to add the following line to our Coldspring.xml file:

view plain print about
1<import resource="/ValidateThis/config/Coldspring.xml.cfm" />

That will give our Coldspring bean factory access to all of VT's beans. Now we can add a ValidateThisConfig bean to our Coldspring config:

view plain print about
1<bean id="ValidateThisConfig"
2    class="coldspring.beans.factory.config.MapFactoryBean">

3    <property name="sourceMap">
4        <map>
5            <entry key="BOValidatorPath"><value>BOValidator</value></entry>
6            <entry key="DefaultJSLib"><value>jQuery</value></entry>
7            <entry key="JSRoot"><value>js/</value></entry>
8            <entry key="defaultFormName"><value>frmMain</value></entry>
9        </map>
10    </property>

This sets a number of default values that VT will use. I explained what these entries do in the Release Notes for version 0.6 of VT, so I won't go into it again here. For the sample app the above values will work. Next we need to add a bean definition for the TDOBeanInjectorObserver:

view plain print about
1<bean id="TDOBeanInjectorObserver"
2    class="ValidateThis.util.TDOBeanInjectorObserver"
3    lazy-init="false">

4    <constructor-arg name="transfer">
5        <ref bean="transfer" />
6    </constructor-arg>
7    <constructor-arg name="afterCreateMethod">
8        <value>setUp</value>
9    </constructor-arg>
10    <property name="beanInjector">
11        <ref bean="beanInjector" />
12    </property>

This sets up Brian Kotek's Transfer Decorator Object Bean Injector Observer (which can be found at his ColdSpring Bean Utilities RIAForge project). In a nutshell that component will allow us to inject other components into our Transfer objects when they are created. Note that the TDOBeanInjectorObserver as well as the generic beanInjector both ship with the VT framework, so if you aren't already using them you don't need to worry about downloading or installing them. If you are already using the TDOBeanInjectorObserver in your application then you won't need to add that bean definition at all. Note also that the constructor-arg afterCreateMethod is being set to "setUp" - we'll come back to that in a bit.

OK, now that our Coldspring setup has been done we can move on to adding a decorator for our User object. To tell Transfer that we want to use a decorator for the User object, we need to make a small change to our transfer.xml file:

view plain print about
1<object name="user" table="tblUser" decorator="model.user">
2    <id name="UserId" type="numeric" />
3    <property name="UserName" type="string" />
4    <property name="UserPass" type="string" />
5    <property name="Nickname" type="string" nullable="true" />

You'll notice that all we've done is added the decorator="model.user" attribute to the User object definition. Now we need to create that decorator, so we'll create a file called user.cfc and place it in the /model directory. Here's what that file will contain:

view plain print about
1<cfcomponent output="false" extends="AbstractTransferDecorator">

Hmm, you may be saying, there's nothing in there. That's true. The fact is that all of the code that you need to add to your object's decorator in order to integrate VT into your object is the same no matter what your object is. The code will be identical in your User object, your Account object, your Product object, etc. So in order to avoid having to repeat that code over and over again, we're going to create an Abstract Transfer Decorator object. You might have noticed that the User decorator shown above does in fact extend another cfc, called AbstractTransferDecorator, so that's where we're going to put the actual integration code. Let's look at that next:

view plain print about
1<cfcomponent output="false" extends="">
3<cffunction name="setUp" access="public" output="false"
4    returntype="void"
5    hint="I am run after configure via the TDOBeanInjector, and I inject the appropriate BOValidator into the object.">

6    <cfset setValidator(getValidationFactory().getValidator(getClassName(),getDirectoryFromPath(getCurrentTemplatePath()))) />
9<cffunction name="onMissingMethod" access="public" output="false"
10    returntype="Any"
11    hint="I am used to help communicate with the BOValidator, which is accessed via getValidator().">

12    <cfargument name="missingMethodName" type="any" required="true" />
13    <cfargument name="missingMethodArguments" type="any" required="true" />
15    <cfset var Validator = getValidator() />
16    <cfset var ReturnValue = "" />
18    <cfif arguments.missingMethodName EQ "validate">
19        <cfset ReturnValue = Validator.validate(theObject=this,argumentcollection=arguments.missingMethodArguments) />
20    <cfelseif StructKeyExists(getValidator(),arguments.missingMethodName)>
21        <cfinvoke component="#Validator#"
22            method="#arguments.missingMethodName#"
23            argumentcollection="#arguments.missingMethodArguments#"
24            returnvariable="ReturnValue" />
25    </cfif>
26    <cfif IsDefined("ReturnValue")>
27        <cfreturn ReturnValue />
28    </cfif>
31<cffunction name="testCondition" access="Public" output="false"
32    returntype="boolean"
33    hint="I dynamically evaluate a condition and return true or false.">

34    <cfargument name="Condition" type="any" required="true" />
36    <cfreturn Evaluate(arguments.Condition)>
39<cffunction name="getValidator" access="public" output="false"
40    returntype="any">

41    <cfreturn variables.Validator />
43<cffunction name="setValidator" access="public" output="false"
44    returntype="void">

45    <cfargument name="Validator" type="any" required="true" />
46    <cfset variables.Validator = arguments.Validator />
49<cffunction name="getValidationFactory" access="public" output="false"
50    returntype="any">

51    <cfreturn variables.ValidationFactory />
53<cffunction name="setValidationFactory" access="public" output="false"
54    returntype="void">

55    <cfargument name="ValidationFactory" type="any" required="true" />
56    <cfset variables.ValidationFactory = arguments.ValidationFactory />

OK, so there's a bit of code there, but the nice thing is that, if you just want to use the framework, you don't really need to understand exactly what it's doing. You can just copy that code as is into your abstract decorator and you're good to go. Of course, if you're already using an abstract decorator you'll have to make sure that none of those methods conflict with any existing methods. For example, if you already have an onMissingMethod() handler in your abstract decorator you'll need to integrate the code above into your existing method. In the interest of keeping these posts short and to the point I'm not going to elaborate on what it's doing right now, but I will discuss it in a future blog post.

At this point we're actually finished integrating the framework into our business object. We're now ready to use it. Hmm, that was even less painful that I imagined ;-)

In my next post I will discuss the changes that we need to make to the sample app to make use of the framework to perform our validations.

A zip that incorporates all of the code changes described in this post is attached.