When you pass several form or URL variables into ColdFusion with the same name, they end up as a comma separated list. This is commonly done with checkboxes – a user can check as many items as they want, then they will end up in your code all in a single variable. In many other languages, Ruby or PHP for example, the selected items will end up in an array. Getting these values as a list usually works fine, until one of your values contains the list delimiter (comma). Take this form, for example:
<form action="test.cfm" method="post"> <p>Select your books:</p> <input type="checkbox" name="books" value="A Wrinkle in Time"> A Wrinkle in Time<br> <input type="checkbox" name="books" value="A Tale of Two Cities"> A Tale of Two Cities<br> <input type="checkbox" name="books" value="The Lion, the Witch and the Wardrobe"> The Lion, the Witch and the Wardrobe<br> <input type="submit" name="submitButton" value="Go"> </form>
If you check the first two boxes and submit the form, you will end up with this in the form scope:
But if you check the third book, which has a title containing commas, things start to get messy:
How can you tell the selections apart!? You can’t. Now, ideally you should be using an ID for the checkbox values instead of a string. In this example maybe you’d use the ISBN number instead of the title. But this is not always possible. Sometimes you are receiving a form post from a third party which you have no control over. I’ve run into situations where the checkbox values contained part numbers, which you would think would be comma-safe, but we did encounter some part numbers that contained commas (don’t get me started…)
But, ColdFusion gives us the power of Java under the hood, and we can reach down into the Java layer to pull out these parameters as a real array! Its very easy:
getPageContext().getRequest().getParameterValues('Your form or url field name')
So lets try it with the ‘books’ field:
Nice! Another interesting method is
getParameterMap(). It gives us Form and URL variables, with each variable coming through as its own array. To show this, I’ve modified the form tag a little to include a URL variable:
<form action="test.cfm?event=someFakeEvent" method="post">
Here is the output of the getParameterMap() method:
As I was wrapping up this blog post another opportunity arose to use this idea at work. We tried this, but for some reason it wasn’t working, we always got an undefined value instead of an array. After some research we discovered this way of pulling parameters only works on regular application/x-www-form-urlencoded encoded forms. When you upload files on a form, as we were in this case, you need to use the multipart/form-data enctype. Which renders this nice trick useless.
But there is another way to get the values as an array when the enctype is set to application/x-www-form-urlencoded. Below is a method that will return a form or URL variable as an array, and it works for either enctype. All values with the given name will be returned, whether they came in on the URL or the Form scope.
<cffunction name="FormFieldAsArray" returntype="array" output="false" hint="Returns a Form/URL variable as an array."> <cfargument name="fieldName" required="true" type="string" hint="Name of the Form or URL field" /> <cfset var tmpPartsArray = Form.getPartsArray() /> <cfset var returnArray = arrayNew(1) /> <cfset var tmpPart = 0 /> <cfset var tmpValueArray = "" > < !--- if the getPartsArray method did not return NULL, then this is a multipart/form-data request, which must be handled as such. ---> <cfif IsDefined("tmpPartsArray")> <cfloop array="#tmpPartsArray#" index="tmpPart"> <cfif tmpPart.isParam() AND tmpPart.getName() EQ arguments.fieldName> <cfset arrayAppend(returnArray, tmpPart.getStringValue()) /> </cfif> </cfloop> </cfif> < !--- Add the values that maybe on the URL with the same name, also if this *wasn't* a multipart/form-data request then the above code did not get any of the data, and the method below will return all of it. ---> <cfset tmpValueArray = getPageContext().getRequest().getParameterValues(arguments.fieldName) /> < !--- that may have returned null, so need to test for it. ---> <cfif IsDefined("tmpValueArray")> <cfloop array="#tmpValueArray#" index="tmpPart"> <cfset arrayAppend(returnArray, tmpPart) /> </cfloop> </cfif> <cfreturn returnArray /> </cffunction>
If you copy and paste the code above you’ll need to fix the comments to get it to execute, I had to mangle them to get them to display properly in this post.