Getting to know Transfer ORM
I realize this post comes at an awkward time. It seems most of the posts about ORMs nowadays all center around Hibernate after the release of ColdFusion 9. However, if like me, you are using some of the open-sourced CFML engine alternatives like Railo or OpenBD, Hibernate support might be on the project road map but it is not here yet. I still have quite a few sites out there running Transfer ORM, so I for one will be supporting it as long as it is available.
First I would like to preface this by saying this is not an in-depth guide to using Transfer ORM. This more or less is a collection of things I wish I had known when I made the switch from writing tons of SQL to Transfer. I fought it for the longest time, but now you could not get me to switch back. I did a lot of reading up on how to use Transfer in the beginning. There are a lot of great posts out there from a lot of great programmers so it is hard to nail down one great resource as there are so many. I will say that the one resource I book marked and went back to quite a bit was Ray Camden’s posts on Transfer (http://www.coldfusionjedi.com/index.cfm/transfer) which covers all the basics of database interactions. Also, Mark Mandel has a pretty comprehensive list on the Transfer Wiki Tutorials section (http://docs.transfer-orm.com/wiki/Tutorials.cfm).
One of the biggest hangups I get from people (and you know who you are 😉 is having to map out your datasources in transfer.xml. On a legacy project I can see this being a little time-consuming, but when you are starting a project using Transfer ORM from scratch it really is not that difficult. Actually, I find myself tabbing over to transfer.xml to examine schema instead of switching over to my db front end.
Decorators are not just for decorating
Decorators in Transfer allow you to modify the way Transfer outputs data or “decorate” it. As a quick example, say our database has a fields for first and last names of users. Each time we output user names we could output it manually:
<cfscript> userObj = application.transfer.get("mysite.user",1); </cfscript> #userObj.getFirstname()# #userObj.getLastname#
That is straight forward enough, but it sure is a lot of typing every time we want to output a name. With decorators, we can define a custom method to automatically do that for us:
<cfcomponent extends="transfer.com.TransferDecorator" output="no"> <cffunction name="getFullName" access="public" returntype="string" output="false"><cfreturn "#getFirstName()# #getLastName()#" /></cffunction> </cfcomponent>
Any code in the decorator is available to the Transfer object, so now by calling the userObj.getFullName() method the users first and last names will be returned. But as I said, decorators are not just for decorating. Say you are beginning a project and if you are like me, you are constantly revising the schema. Much like you would have to update all SQL that saves data, you also have to update the save method in your model to accommodate the extra fields. However, Transfer will allow us to take a short cut. Say we have a base decorator like so:
<cfcomponent extends="transfer.com.TransferDecorator" output="no"> <cffunction name="populate" access="public" returntype="void" output="false"> <cfargument name="propertiesmap" type="struct" required="true" /> <cfset var propertyname = "" /> <cfloop collection="#arguments.propertiesmap#" item="propertyname"> <cfif StructKeyExists(this, "set" & propertyname)> <cfinvoke method="set#propertyname#"> <cfinvokeargument name="#propertyname#" value="#arguments.propertiesmap[propertyname]#" /> </cfinvoke> </cfif> </cfloop> </cffunction> </cfcomponent>
This decorator has a method called populate() that is available to our Transfer object. It accepts one argument, a structure namedpropertiesmap, and it will attempt to match the structure keys to Transfer set method. If a match is found it will update the Transfer object, otherwise the key is ignored. You can then call the Transfer objects save() method and update the database. As a quick example, supposing a user was updating their information on a form and posted it to a processing page I would handle the database interaction like so:
isDefined("form.userid")> <cfscript> userObj = application.transfer.get("mysite.user",form.userid); userObj.populate(FORM); application.transfer.save(userObj); </cfscript> </cfif>
I would love to take credit for this, but it actually came from John Whish. I generally use this as my base decorator and if I need to add extra functionality for an object I will extend my base decorator.
Finally, when getting in to Transfer I wish I had known about the configure() method. As the name may allude to, this will allow you to set up some default values for your Transfer object.
If you are looking to get in to Transfer or have thought about it, I hope this helps you out. I look forward to Hibernate support in Railo, but I do love me some Transfer ORM.