Using Transfer's MetaData to construct queries

December 15, 2008

Thanks to Bob Silverberg's excellent series of posts on How I use Transfer, I've refined how my model is constructed by taking his ideas and modifying them to suit my own style. One of the things I wanted to be able to do with transfer is set up a basic search in my abstract Gateway (which I can override in my concrete gateway if I need to).

Because my Abstract gateway doesn't know which object I'm trying to search I needed to pass the object properties to my search. I'm lazy and don't like to write more code than I need to so I figured that I could use Transfer's metadata to provide me with all the information that I needed.

This is what I've come up with, it still needs a bit of work but the concept seems to work well and means I only need to update my table information in my Transfer.xml config for my Abstract Gateway to pick up the new metadata.



<cfcomponent type="abstract"
displayname="AbstractGateway"
output="false">


<!---
...standard init, new, getObject, delete, getList, save methods...
--->


<cffunction name="getSearchResults"
access="public"
output="false"
returntype="query">


<cfargument name="propertymap"
type="struct"
required="true"
hint="This could be the form scope" />

<cfset var TQL = "" />
<cfset var query = "" />
<cfset var metadata = getProperties() />
<cfset var property = "" />
<cfset var index = "" />

<cfsavecontent variable="TQL">
<cfoutput>
FROM #getTransferClassName()# AS transferClass
WHERE transferClass.id = transferClass.id
<cfloop from="1" to="#ArrayLen( metadata )#" index="index">
<cfset property = metadata[ index ] />
<!--- check that the key passed is a property of the object in case the form scope was passed --->
<cfif StructKeyExists( arguments.propertymap, property.name )
AND StructFind( arguments.propertymap, property.name ) neq "">

AND transferClass.#property.name# <cfif arguments.propertymap.type eq "string">like<cfelse>=</cfif> :#property.name#
</cfif>
</cfloop>
ORDER BY transferClass.#getDescColumn()#
</cfoutput>
</cfsavecontent>

<cfset query = getTransfer().createQuery( TQL ) />

<!--- set up the params for transfer --->
<cfloop from="1" to="#ArrayLen( metadata )#" index="index">
<cfset property = metadata[ index ] />
<cfif StructKeyExists(arguments.propertymap, property.name)
AND StructFind( arguments.propertymap, property.name ) neq "">

<cfset query.setParam( property.name, StructFind( arguments.propertymap, property.name ) ) />
</cfif>
</cfloop>
<cfreturn getTransfer().listByQuery( query ) />
</cffunction>


<cffunction name="getTransfer"
access="public"
output="false"
returntype="any">

<cfreturn variables.Transfer />
</cffunction>
<cffunction name="setTransfer"
returntype="void"
access="public"
output="false">

<cfargument name="transfer"
type="any"
required="true" />

<cfset variables.Transfer = arguments.Transfer />
</cffunction>
<cffunction name="getTransferClassName"
access="public"
output="false"
returntype="any">

<cfreturn variables.TransferClassName />
</cffunction>
<cffunction name="setTransferClassName"
returntype="void"
access="public"
output="false">

<cfargument name="TransferClassName"
type="any"
required="true" />

<cfset variables.TransferClassName = arguments.TransferClassName />
</cffunction>

<!---
-----------------------------------------------------
Methods for Building Metadata
-----------------------------------------------------
--->

<cffunction name="setProperties"
access="private"
output="false"
returntype="void">

<cfset var thisProperty = "" />
<cfset var propertyAttributes = "" />
<cfset var abstractList = getTransfer().getTransferMetaData( getTransferClassName() ).getPropertyIterator() />

<cfset variables.properties = [] />

<cfloop condition="#abstractList.hasnext()#">
<cfset thisProperty = abstractList.next() />

<cfset propertyAttributes = {} />
<cfset propertyAttributes.name = thisProperty.getName() />
<cfset propertyAttributes.type = thisProperty.getType() />
<cfset propertyAttributes.required = NOT thisProperty.getIsNullable() />
<cfif thisProperty.hasNullValue()>
<cfset propertyAttributes.default = thisProperty.getNullValue() />
<cfelse>
<cfset propertyAttributes.default = "" />
</cfif>

<cfset ArrayAppend( variables.properties, propertyAttributes ) />
</cfloop>
</cffunction>
<cffunction name="getProperties"
access="private"
output="false"
returntype="array">

<cfif NOT StructKeyExists( variables, "properties" )>
<cfset setProperties() />
</cfif>

<cfreturn variables.properties />
</cffunction>

</cfcomponent>

Comments welcome!


2 comments

  1. I found this post very useful! I am working on a Mach-II filter to convert the data submitted through a form to transfer beans automatically and stick them in the event object. I was having problems because getMetadata wasn't working as I expected on Transfer objects ...

    so I just plugged in a temp solution and parsed the transfer.xml file to determine the correct properties. If I'm not mistaken though, how you did it is the way I need to approach this. Thanks for posting!

    Comment by Brian FitzGerald – December 15, 2008
  2. Hi Brian. Always good to know that someone has found a post interesting.
    You should be able to get all the info you need from the getTransferMetaData() method instead of parsing the XML separately.
    Let me know how it goes!

    Comment by John Whish – December 16, 2008

Leave a comment

If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)

Please note: If you haven't commented before, then your comments will be moderated before they are displayed.