Photo from Chile

Dynamically Calling CFC Methods with Nulls

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:

view plain print about
1<cfcomponent persistent="true" entityname="User" table="tblUser" >
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:

view plain print about
1<cfset User = EntityLoad("User",1,true) />
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:

view plain print about
1<cfloop list="UserId,UserName,DateTerminated" index="prop">
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:

view plain print about
1Attribute validation error for CFINVOKEARGUMENT.
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:

view plain print about
1<cfloop list="UserId,UserName,DateTerminated" index="prop">
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:

view plain print about
1<cffunction name="_setProperty" access="private" returntype="void" output="false">
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:

view plain print about
1<cfset User = EntityLoad("User",1,true) />
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!

TweetBacks
Comments
Thanks for this.
# Posted By John Allen | 9/2/09 10:52 AM
nice one, thks
# Posted By guillaume | 5/31/13 2:49 PM