Dynamically Calling CFC Methods with Nulls
Posted At : September 2, 2009 12:05 AM | Posted By : Bob Silverberg
Related Categories: ColdFusion, CF ORM Integration
With the advent of ColdFusion 9's ORM integration, there is now a very real need to call methods and pass nulls to them. For example, you may have a User object that allows for nulls in a DateTerminated property, like so:
2 <cfproperty name="UserId" fieldtype="id" generator="native" />
3 <cfproperty name="UserName" notnull="true" />
4 <cfproperty name="DateTerminated" type="date" notnull="false" />
5</cfcomponent>
If I want to pass a null value into the setter for the DateTerminated property, I can do so using the JavaCast function, like so:
2<cfset User.setDateTerminated(JavaCast("NULL","")) />
But what if I want to call that method dynamically? What if I want to loop through a list of properties and pass a value into each of them? Generally, when I need to call a method dynamically, I'd do it using cfinvoke, like so:
2 <cfinvoke component="#this#" method="set#prop#">
3 <cfinvokeargument name="#prop#" value="#someValue#" />
4 </cfinvoke>
5</cfloop>
The problem with that approach is that, if I put JavaCast("NULL","") into someValue, then ColdFusion throws an error:
2It requires the attribute(s): VALUE.
It does this because, as far as CF is concerned, when I put a null into the value attribute it actually disappears from the attributes of the tag. There are other ways of dynamically invoking methods, and one of them works well in my specific scenario, which is that I'm calling methods from within the given object. This method involves creating a pointer to a function and then calling that. The code above would change to something like this:
2 <cfset myMethod = this["set#prop#"] />
3 <cfset myMethod(someValue) />
4</cfloop>
In my specific use case I'm not simply looping over a list and calling setters, I'm doing a bunch of different things to different properties, and I didn't want to duplicate that logic throughout my method. So I created a function in my cfc called _setProperty(), like so:
2 <cfargument name="name" type="any" required="yes" />
3 <cfargument name="value" type="any" required="false" />
4 <cfset var theMethod = this["set" & arguments.name] />
5 <cfif IsNull(arguments.value)>
6 <cfset theMethod(JavaCast("NULL","")) />
7 <cfelse>
8 <cfset theMethod(arguments.value) />
9 </cfif>
10</cffunction>
Now I can call my _setProperty() function from within another method in my cfc, and if I want to set the property to null, I just simply do not pass a value. For example, to set the DateTerminated property to null, I'd just do:
2<cfset User._setProperty("DateTerminated") />
This has all come in handy for the populate() method that I'm working on in my Base Persistent Object. Note that my use of the isNull() function will only allow the above code to run on CF9. It can easily be changed to run on CF7 and CF8 by changing that to use isDefined(). Oh, and I have Elliott Sprehn to thank for this idea. I spotted it in a comment on Ben Nadel's blog. Thanks guys!