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:

<cfcomponent persistent="true" entityname="User" table="tblUser" >
    <cfproperty name="UserId" fieldtype="id" generator="native" />
    <cfproperty name="UserName" notnull="true" />
    <cfproperty name="DateTerminated" type="date" notnull="false" />
</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:

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

<cfloop list="UserId,UserName,DateTerminated" index="prop">
    <cfinvoke component="#this#" method="set#prop#">
        <cfinvokeargument name="#prop#" value="#someValue#" />
    </cfinvoke>
</cfloop>

The problem with that approach is that, if I put JavaCast("NULL","") into someValue, then ColdFusion throws an error:

Attribute validation error for CFINVOKEARGUMENT.
It 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:

<cfloop list="UserId,UserName,DateTerminated" index="prop">
    <cfset myMethod = this["set#prop#"] />
    <cfset myMethod(someValue) />
</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:


<cffunction name="_setProperty" access="private" returntype="void" output="false">
    <cfargument name="name" type="any" required="yes" />
    <cfargument name="value" type="any" required="false" />
    <cfset var theMethod = this["set" & arguments.name] />
    <cfif IsNull(arguments.value)>
        <cfset theMethod(JavaCast("NULL","")) />
    <cfelse>
        <cfset theMethod(arguments.value) />
    </cfif>
</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:

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

Comments
John Allen's Gravatar Thanks for this.
# Posted By John Allen | 9/2/09 10:52 AM