The last piece of the puzzle for Validat is a concept called Data Transformers. When we were researching the requirements for Validat, I made several blog postings trying to determine how people handled validation. One of the things that came out of those postings was that the data being validated was not always in a structure such as the form scope. Some people liked to take data and stuff it into a bean or business object and then validate the object while others prefer to validate the data before they use it. Then, there is always the unknown edge case and the goal was really to make Validat work in any situation where you needed data validation. The answer we came up with to this question is what we call Data Transformers.
What is a Data Transformer?
A data transformer simply transforms data – novel huh? The Validat data validation engine validates data in a key / value structured format. So, if you are validating data in the form structure, the data transformer does little more than make a copy of the form structure and pass it to the validation engine. However, if you are validating data in a business object, the data transformer extracts the data from that business object, stores it in a structure and passes that structure to the validation engine.
Adhering to the concept of abstraction, a data transform is implemented as a simple ColdFusion component that follows a prescribed interface. Not wanting to limit the use of Validat to ColdFusion 8, we chose to create a single component called transformer.cfc, which all data transformer components extend.
So, when it comes time to validate data that is in a different format, it is simply a matter of creating a new transformer component that follows the prescribed interface, registering that component, and you are off and validating.
Within each transformer component are two functions – an init constructor and a function called getData. It is possible for other functions to be added to support the data retrieval process, but these two functions are all that is required by the API. If you open up any of the transformer objects that come with Validat, you will notice that the init function does not do much in most cases – the bulk of the work is handled in the getData function.
Deconstructing a Transformer
As mentioned before, the structure transformer does not do much, so lets walk through the bean transformer to see how it works.. The following code is contained in the transformBean.cfc component (comments have been stripped).
hint="ColdFusion bean data transformer."
<!— ———————————————————— —>
<!— constructor —>
<cffunction name="init" access="public" returntype="transformBean"
hint="The default constructor for the transformer object, returning the initialized transformer object instance">
<!— call the base constructor —>
<cfset super.init() />
<!— return the initialized transformer object —>
<cfreturn this />
</cffunction> <!— end: init() —>
<!— ———————————————————— —>
<!— public methods —>
description: Retrieves the data values in the form of key / value pairs from the bean object.
<cffunction name="getData" access="public" output="false" returntype="struct"
hint="Retrieves the data values in the form of key / value pairs from the bean object.">
<cfargument name="dataCollection" type="any" required="true" hint="The data collection to be transformed" />
<!— setup temporary variables —>
<cfset var beanMetaData = getMetaData(arguments.dataCollection) />
<cfset var resultStruct = structNew() />
<cfset var funcPtr = 0 />
<!— loop over the meta data functions, looking for getters —>
<cfloop from="1" to="#arrayLen(beanMetaData.functions)#" index="funcPtr">
<!— if the function appears to be a getter —>
<cfif left( beanMetaData.functions[funcPtr].name, 3 ) EQ "get" >
<!— call the getter function and insert the value into the result structure —>
<cfset structInsert( resultStruct, beanMetaData.functions[funcPtr].name, evaluate( "arguments.dataCollection.#beanMetaData.functions[funcPtr].name#" ), true ) />
</cfif> <!— end: if the function appears to be a getter —>
</cfloop> <!— end: loop over the meta data functions, looking for getters —>
<!— return the result structure —>
<cfreturn resultStruct />
</cffunction> <!— end: getData() —>
In the <cfcomponent tag, the only thing to take notice of is the fact that we are extending the base transformer.cfc component which is located in the same transformers folder as the bean transformer.
The first method shown here is the init constructor which is public and returns the current transformBean object instance after any confirmation has been completed. In this example, the only operation this function performs is a call to super.init() which calls the init function on the extended transformer object.
The real work of any transformer object is in the getData function. The getData function takes as an argument a data collection of any type and returns a structure of key / value pairs extracted from the data collection. In this case, the data collection represents a business object from which the transformer will be extracting data.
The way the bean transformer works is that it gets the meta data for the business object and from that meta data, extracts any functions that begin with the word ‘get’. If the business object was build using "standard" development practices, there should be getter and setter methods for every data element in the bean. Therefore, the transformer finds all of the getters and then one by one, calls each getter and inserts the returned value into a structure.
The transformer now has a structure of key / value pairs that can be returned to the Validat data validation engine for validation.
The Big Picture
Like validator objects, data transformer objects require registering. After creating a new data transformer object, the first thing that needs to happen is that the transformer object should be registered with the Validat ColdSpring XML configuration file (/scr/config/validat.xml). In this configuration file, there is a section for transformers and the included transformer objects are setup here. To add a new transformer object, just add a new line like below to the validat.xml file.
<bean id="transformBean" class="validat.dataTransformers.transformBean" />
The bean id value must be unique, but can be anything of your choosing. The class value is the dot notation path to your transformer cfc. Remember your bean id as you will need it in the next part.
Once the transformer is registered with the ColdSpring bean factory, it must be connected to one or more data sets in order to be utilized. The data definition contains all of the data sets, which if a transformer is not specified, will utilize the basic form structure transformer. A data set definition with a transformer specified looks like this.
<data-set name="user" transformer="transformBean">
When validate function for Validat is called, it accepts two arguments – a data collection and a data set name to validate that data collection against. The first thing Validat does is look at the data definition for the specified data set name to determine which data transformer it needs to use to extract the data from the data collection. In the above case, the transformer attribute of the data-set element indicates that when this data set is validated, Validat should utilize the transformer object with bean id ‘transformBean’ to extract the data from the data collection. Once the data is extracted, Validat moves on with the validation process for all of the data elements contained in the data set.
Data Transformers and Validators are what make the Validat data validation engine so powerful and give it the ability to be dropped into virtually any scenario. If you have a custom situation where you need validation, it is a very quick an easy process to write a data transformer that fits your requirements and to drop it into Validat. Then you have instant data validation.
In the next part of this series, we will setup a working example where we use Validat to validate the data coming from a form and show exactly how it all works together. In the mean time, check out the Validat project page (http://trac.alagad.com/Validat) and join the Validat mailing list (http://groups.google.com/group/Validat). Until then ….