If you’ve been following this blog, you know I’ve been trying to get TransferObjects to function as Mach-II event-beans. A problem that has been solved, I’m happy to say, with the help of Transfer Decorators.
The approach I took was to supplement my TransferObjects with methods that would only be used by the event-bean during form-to-bean population, and only for non-string methods. For example, my Course object has a ‘cost’ associated with it, which is a numeric. I added the following to my Course decorator:
<cffunction name="getCostString" returntype="string" output="false"> <cfreturn getCost()/> </cffunction> <cffunction name="setCostString" returntype="void" output="false"> <cfargument name="costString" type="string" required="true"/> <cfif isNumeric(arguments.costString)> <cfset setCost(arguments.costString)/> </cfif> </cffunction>
Note that the functions and argument both reference a costString instead of just a cost. The costString will only be used by the event-bean population code, which means you need to modify your form:input tag as well:
<label for="costString">Cost to Participants</label> $<form:input path="costString"/>
Once this code is in place, the event-bean works like a charm, populating all the fields you need without throwing those pesky errors due to string-to-numeric or string-to-date mistypes. It even works for checkboxes:
<label for="educationFormatString">Education Format</label> <cfloop query="educationFormatQuery"> <form:checkbox path="educationFormatString" value="#educationFormatQuery.id#"/>#educationFormatQuery.format# </cfloop>
<cffunction name="getEducationFormatString" returntype="string" output="false"> <cfset var returnString = ""/> <cfset var formats = getEducationFormatsArray()/> <cfloop from="1" to="#ArrayLen(formats)#" index="i"> <cfif returnString neq ""><cfset returnString = returnString & ","/></cfif> <cfset returnString = returnString & formats[i].getId()/> </cfloop> <cfreturn returnString/> </cffunction> <cffunction name="setEducationFormatString" returntype="void" output="false"> <cfargument name="educationFormatString" type="string" required="true"/> <cfset var array = ListToArray(arguments.educationFormatString)/> <cfset clearEducationFormats()/> <cfloop from="1" to="#ArrayLen(array)#" index="i"> <cfset addEducationFormats(getTransfer().get("gorgon.EducationFormat", array[i]))/> </cfloop> </cffunction>
Since I’m using decorators anyway, why not just overwrite the methods for setting/getting numeric/date/checkboxes and be done with it? Well, I figured the solutions for populating event-beans and regular TransferObject usage might be completely different depending on the complexity of the bean; why go cluttering up a bean with extraneous method overloading and type-checking when I could just add a couple of methods that are only specifically used by one instance of the framework, and then ignored the rest of the time? Basically it’s a trade-off, and neither side is well explored territory so I erred on what I thought was the side of caution. I’m completely open to trying it the other way if the data suggest I made a design mistake.
So there you have it, after three iterations of attempts I’m finally binding forms to TransferObjects without writing too much code. Now we get to move out of the kiddy-pool and start coding for real. Hopefully this project will go forward more smoothly and will be much more maintainable now that I have what I consider a clean design from the start.
We shall see.