argumentCollection - learn it and love it!

February 16, 2010

I've been working on a project that someone else wrote recently and, whilst the code works, I'm finding things that I think could be improved by knowing a few of the cool features Coldfusion has. I'm posting this in case other developers aren't aware of what you can do.

Last time I blogged about per-application mappings, this time it is the turn of the argumentCollection parameter!

Take the following simple code example:


<cfcomponent output="false"
hint="I am a generic DAO (singleton) that gets data from the database">


<cffunction name="init"
output="false"
returntype="GenericDAO">

<cfargument name="datasource"
required="true"
type="string">

<cfset instance.datasource = arguments.datasource>
<cfreturn this>
</cffunction>

<cffunction name="findByFilter"
output="false"
returntype="query">

<cfargument name="deptid"
required="false"
type="numeric">

<cfargument name="prefix"
required="false"
type="string">


<cfset var result = "">

<cfquery name="result" datasource="#instance.datasource#">
SELECT personid, deptid, firstname, surname, extension
FROM Employees
WHERE 1 = 1
<cfif StructKeyExists( arguments, "deptid" )>
and deptid = <cfqueryparam value="#Val( arguments.id )#" cfsqltype="cf_sql_integer">
</cfif>
<cfif StructKeyExists( arguments, "prefix" )>
and firstname LIKE <cfqueryparam value="#( arguments.prefix )#%" cfsqltype="cf_sql_varchar">
</cfif>
</cffunction>

<cfreturn result>
</cffunction>

</cfcomponent>

The code that was calling this CFC to get a list of employees looked something like this:


<cfif StructKeyExists( url, "department" )>
<cfif StructKeyExists( url, "startswith" )>
<cfset q = application.genericDAO.findByFilter( deptid=url.department, prefix=url.startswith )>
<cfelse>
<cfset q = application.genericDAO.findByFilter( deptid=url.department)>
</cfif>
<cfelseif StructKeyExists( url, "startswith" )>
<cfset q = application.genericDAO.findByFilter( prefix=url.startswith )>
<cfelse>
<cfset q = application.genericDAO.findByFilter()>
</cfif>

Now that is a lot of code to do something really simple - and if you add more arguments, it will get even messier. ColdFusion allows you to pass an struct of values to your methods, using a parameter calling argumentCollection. This allows us to rewrite the previous code to look like this:


<cfset args = {}>

<cfif StructKeyExists( url, "department" )>
<cfset args.deptid = url.department>
</cfif>
<cfif StructKeyExists( url, "startswith" )>
<cfset args.prefix = url.startswith>
</cfif>

<cfset q = application.genericDAO.findByFilter( argumentCollection=args )>

Now that is much easier to read and maintain!


5 comments

  1. Great post!

    I would just add that cfinvoke is another option to accomplish the same thing:

    <cfinvoke returnvariable="q" component="#application.genericDAO#" method="findByFilter">
      <cfif StructKeyExists( url, "department" )>
        <cfinvokeargument name="deptid" value="#url.department#">
      </cfif>
      <cfif StructKeyExists( url, "startswith" )>
        <cfinvokeargument name="prefix" value="#url.startswith#">
      </cfif>
    </cfinvoke>

    I think the choice comes down to personal preference, but they are both good options to have.

    Comment by Steve Bryant – February 16, 2010
  2. Hey Steve! I've never actually used cfinvoke like that, but it's a good alternative way to do it. Thanks for the comment :)

    Comment by John Whish – February 17, 2010
  3. ArgumentCollection is definitely cool; it took me a while to accept the fact that I had to use the "name argument" - argumentCollection - to get it to work; but once I accepted that, it was sweet!

    To add to @Steve's comment, I believe argumentCollection will also work as part of the CFInvoke tag itself:

    <cfinvoke argumentcollection="..." />

    Comment by Ben Nadel – February 17, 2010
  4. Absolutely right, Ben. Though I don't see an advantage to cfinvoke with argumentcollection unless the method name is also dynamic.

    I wonder if it is possible to combine argumentCollection with named arguments in cfinvoke. Something to test out...

    Comment by Steve Bryant – February 18, 2010
  5. Pretty cool!

    You can use cfinvoke with both argumentCollection and cfinvokeargument. You can even do so with each setting the same argument (cfinvokeargument wins).

    I haven't thought of a use for this yet, but I am glad to know it.

    Comment by Steve Bryant – February 18, 2010

Leave a comment

If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)

Please note: If you haven't commented before, then your comments will be moderated before they are displayed.