Dealing with Form and URL Variables

It’s not terribly uncommon to have a page that is expecting a particular variable, but the page may not know if that variable will be in the form scope or URL scope. Case in point might be a resultset that does pagination. On the first submit, the variables would likely be in the form scope. As the user pages through the results, it’s entirely possible that the variables would be in the URL scope.

The most straightforward way to handle this would be:

<cfparam value="myNewVar" default="" />
<cfif structKeyExists(URL, 'myVar')>
	<cfset variables.myNewVar = URL.myVar />
<cfif structKeyExists(form, 'myVar')>
	<cfset variables.myNewVar = form.myVar />

It checks both scopes for the var, and if it finds the variable in either, it assigns it to a new local variable. It’s straightforward, but not particularly “clean”.

The first place that I saw this handled more elegantly was in Fusebox. Fusebox takes both form and URL scopes and places any variables found there into the “attributes” scope. In the case of a collision (if a variable exists in both scopes), you can specify which one should take precedence. Cleaner.

Other frameworks take a similar approach. Both Model-Glue and Coldbox put form and URL vars into the event “scope”.

So that’s all well and good… but what if you’re not using a framework? The question came up on cf-talk, and my answer was to use a custom tag that mimics the way Fusebox does it. Then 2 people (Aaron Rouse and Dominic Watson) demonstrated the most ridiculously simple/straightforward way to handle this. A definite headslap “duh, Charlie” moment. Here’s the code:

<cfset myAttributes = structNew() />

<cfset structAppend(myAttributes, URL) />
<cfset structAppend(myAttributes, form) />

That’s it. No conditionals, No custom tags.

So… what’s going on here?

As of ColdFusion mx (6.0), all scopes are structures. So we first create a new empty structure. Think of this as a container that we’ll use to hold the data from both the form and URL scopes/structures. Using the built in structAppend() function, we’re simply adding URL and form variables into that container. Now, in much the same way that Fusebox uses the “attributes” scope, and Model-Glue/Coldbox use the “event” scope, we can refer to the variables in the myAttributes “scope”, as it will contain data from both form and URL.

One thing to point out is that structAppend() takes an optional 3rd argument (overwriteFlag). By default, this is set to ‘true’. So, similar to way that the frameworks allow you to specify which scope should take precedence in the case that the same variable exists in both, the order in which you write your structAppends() is very important. If you want the URL scope to take priority, append the URL scope last. If you want the form scope to take priority, append the form scope last. Whichever scope/struct is appended last will effectively overwrite the existing variable, giving that scope the “higher priority”.