Ask a Jedi: Structure as an Argument

Joel asks:

This is just a quick "best practices" question about CFC's, specifically regarding arguments. Let's say that I am creating a book entry in my database. My form has 3 simple fields--title, author, number of pages. Easy enough. My question is what a "best practice" for dealing with this scenario would be. Although I am still pretty new to CFC's, I have learned that bundling information as structs to be used in the CFC can be extremely useful, especially when the contents of said struct are dynamic. But what of known values? Obviously, when invoking the component I could bundle title, author and number of pages into a struct and look for that in the CFC. However, in doing this, it would seem that the various specifications for individual arguments (i.e., 'required', 'type', etc.) would be lost as these could only be applied to the expected struct. Also, it seems that the whole idea of encapsulation is compromised to an extent as the specificity of the arguments being expected is reduced and made more generic (e.g., the struct). On the other hand, when dealing with ridiculously long forms full of known values, loading them all into a struct would potentially save some coding on the CFC side.

So just in case folks don’t quite get what Joel is saying - he is talking about two options to pass data to a CFC. You can either use a set of arguments or have one argument that is a struct. Consider the following two examples:

<cffunction name=”test” access=”public” returntype=”string” output=”false”> <cfargument name=”name” required=”true” type=”string” hint=”The name of the user.”> <cfargument name=”age” required=”true” type=”numeric” hint=”The age of the user.”>

&lt;cfreturn "Hello, #arguments.name#, I'm happy you are #arguments.age# years old."&gt; &lt;/cffunction&gt;

<cfset res = test(“Ray”,34)> <cfoutput>#res#</cfoutput> </code>

This is how most people set up their methods and here is the alternate way (struct as arguments) that Joel mentioned:

<cffunction name="test2" access="public" returntype="string" output="false"> <cfargument name="data" type="struct" required="true" hint="Struct of arguments.">

&lt;cfreturn "Hello, #arguments.data.name#, I'm happy you are #arguments.data.age# years old."&gt; &lt;/cffunction&gt;

<cfset s = {name=”Ray”, age=34}>

<cfset res = test2(s)> <cfoutput>#res#</cfoutput> </code>

So as you can see - Joel is right. In the second example, you no longer have any real clues as to what the UDF is using, outside of one structure. You also lose the validation that helpfully double checked name and age. So in my simple example, I definitely think Joel is right - using “real” arguments does make sense. But as he points out - what about a method that takes many more attributes? It would be simpler, for example, to be able to pass the FORM scope as a structure and not use a lot of…

form.name,form.age,form.foo,form.goo,form.rustillreading

Luckily there are a few things we can to make this easier.

First off - don’t forget that you can use CFINVOKE and CFINVOKEARGUMENT syntax. While this results in more typing (potentially), it does make things readable. And you can use these tags without typing until the cows come home. Consider this example:

<cfinvoke component="#application.foo#" method="doIt" returnVariable="result"> <cfloop item="key" collection="#form#"> <cfinvokeargument name="#key#" value="#form[key]#"> </cfloop> </cfinvoke>

All I’ve done here is treat the form scope as a structure where each name reflects an argument in my CFC method. You can, if you want, add conditional logic to hide certain form fields, like submit button names, but if your CFC isn’t using them, they will simply be ignored. And don’t forget the use of attributeCollection:

<cinvoke component="#application.foo#" method="doIt" returnVariable="result" attributeCollection="#form#">

This example is equivalent to the previous form - just more inline and less typing, but again if you did want to skip certain form fields you would not be able to.

So all in all - I agree with Joel here and hopefully the examples above will show ways to mitigate cases where you have to deal with a large number of attributes.

Raymond Camden's Picture

About Raymond Camden

Raymond is a developer advocate. He focuses on JavaScript, serverless and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Comments