2

I am trying to construct a coldfusion conditional statement that looks for incremental form ID checkboxes to have been selected. All checkboxes are defined as Component[component number]. I have established a loop that is looking for a URL variable that is different for every form that calls on the condition as seen below. The issue I am having is that I recieve an error when executing that tells me "Complex constructs are not supported with function parameterexists."

Clearly it has to do with the dynamic nature of the parameterexists statement, but I do not fully know what this means. Can anyone explain this and also offer a solution? I am fairly new to coldfusion and coding, so take it easy on me.

<cfloop from="1" to="#URL.loopcounter#" index="loopvar">

<cfif parameterexists(Form.Component#loopvar#)>

    INSERT INTO Results (MP_Barcode, Reworked, Reworked_By)
    VALUES ('#Form.MontaplastBarcode#', 'YES', '#URL.BadgeNumber#')

</cfloop>

    <cfoutput>
        <p class="success">YOUR REWORK HAS BEEN SUBMITTED SUCCESSFULLY.</p>
    </cfoutput>
<cfelse>
        <p class="error">NO REWORK WAS SUBMITTED. NO COMPONENTS SELECTED.</p>
</cfif>

Depending on the form that calls on this action, the URL loopcounter variable could range from 1 to 50.

  • 1
    My suggestion would be to forget that you ever heard about `parameterExists()` and use something more recent like `structKeyExists()`. `parameterExists()` has been "deprecated" for a bazillion years. – Shawn Apr 13 '18 at 13:44
  • 1
    Also look into SQL Injection, `cfqueryparam` and XSS on inserts. You should never insert anything into a database that comes from an untrusted source like a FORM or a URL. – Shawn Apr 13 '18 at 13:48
  • Also, your example doesn't appear to really be doing anything with the `form.ComponentX` variable. You'll just be inserting the same `Results` values multiple times. Take a step back and explain what you have and what you want to do with it. We'll be able to help if we better understand the whole problem. – Shawn Apr 13 '18 at 13:59
  • 1
    And if you are new to CF, I'd recommend taking a look at http://www.learncfinaweek.com. It's a couple of versions out of date, but it still has good info. If your resource is mentioning `parameterExists()`, it is likely very old and will teach you some very bad habits. – Shawn Apr 13 '18 at 14:07
  • Generally, when CF says "Complex constructs" it's referring to a struct (i.e. object) as opposed to a Simple value like an string. As stated above, structKeyExists() would possibly be a better function to use. Or if you are looking for a simple value, isDefined() is an option. – snackboy Apr 13 '18 at 14:53
  • Yeah, what Shawn said... all of it :) Though just to answer your question about the deprecated ParameterExists() function it's complaining about the dynamic variable `#loopvar#`. ParameterExists() doesn't evaluate input, so it doesn't know how to translate `Form.Component#loopvar#` into something it can use. Though again, that function should NOT be used anymore. It was replaced by better functions like `structKeyExists()` (for checking existence) and associative array notation getting dynamic values `#someStruct["dynamicKeyName"]#`. – SOS Apr 13 '18 at 14:58
  • 1
    @snackboy You have to be careful with `isDefined()`. You can create `variables.my.foo`, and you'd think that `my.foo` exists in the `variables` scope, but `isDefined("my.foo")` will return `NO`. – Shawn Apr 13 '18 at 15:30
  • @Shawn Are you sure that's the case? Just ran a little test ` ` and both values are 'YES' (using CF11) – snackboy Apr 13 '18 at 15:40
  • @snackboy - Are you sure? The 2nd test returns NO on trycf.com in CF10-2016 – SOS Apr 13 '18 at 16:00
  • @snackboy If that's true, then now there's an additional problem. We've got inconsistent behavior. To me, that's worse than unexpected behavior. – Shawn Apr 13 '18 at 16:22
  • Thanks for all the feedback. I guess I am confused on how to apply the structKeyExists() in this particular scenario. If each form id starts with "Component" followed by a number which I was dynamically trying to define (#loopvar#), then how I would I approach this? – Brandon Heinrich Apr 13 '18 at 16:27
  • @BrandonHeinrich - We can easily show you how to do that, but ... it would still be helpful to know the end goal, since there may be several options (possibly better options) depending on what you're really doing. – SOS Apr 13 '18 at 16:33
  • I have multiple CFM files that contain forms (all similar in structure) with checkboxes that range anywhere from 1 to 50. On every cfm file, the checkbox inputs are named "Component" and then followed with a number 1-50. When the submit button is hit on any of these cfm files, they will come back to the same cfm file where this action parameter is (, check to see if the component checkbox has been selected, then execute the actions if the condition is true. @Ageax – Brandon Heinrich Apr 13 '18 at 16:46
  • The actions of the conditional statement (the SQL query in this case) aren'tof concern right now, checking to see if the condition is true (checkbox selected) is the biggie. The loopcounter is just a variable I define in the URL equal to the number of checkboxes of that particular cfm file. – Brandon Heinrich Apr 13 '18 at 16:46
  • @BrandonHeinrich - Yeah, we understand the code. What I'm asking is what the form does, like "I'm displaying a bunch of work orders and want to allow users to check one or more, then insert the id's of the selected work orders into a table".... – SOS Apr 13 '18 at 16:54
  • 1
    @Ageax Sorry :P I tend to over explain without explaining. I am coming up with a rework station for an assembly line. When the user goes to the particular rework form for a part, they will select the components they reworked. The end goal is to insert the checked components into an already existing table. – Brandon Heinrich Apr 13 '18 at 17:09
  • 1
    So you have a bunch of checkboxes each with a different name? It would be easier to leave them all the same name, then on submit, that will pass a comma-delimited list of values of the checkboxes that are checked. Then you can process that list. I still don't understand your loop. You aren't changing anything you insert on each iteration of the loop. – Shawn Apr 13 '18 at 17:09
  • @Shawn - I don't know if I'd consider it inconsistent or corrected. I tried the test on trycf.com and it came up with Yes / No on the CF11 selection there. But then I tried on my personal development site (at Hostek running CF11) and it came up Yes / Yes. Here it at work running CF11 it comes up Yes / Yes. Very odd behavior if tryCF is really switching to CF11. – snackboy Apr 13 '18 at 17:29
  • What is your database type and version? – Shawn Apr 13 '18 at 17:36
  • 1
    @snackboy I don't currently have access to an actual version of CF that I can test on, so I'll take your word for it. If that's the case, you might want to log a bug with TryCF. Regardless, in most cases, I'd still use `structKeyExists()` over `isDefined()`, mainly because `structKeyExists()` is very explicit in what its intent is. `isDefined()` leaves a lot of assumption. – Shawn Apr 13 '18 at 17:39
  • (Edit) @BrandonHeinrich - So do you also need to insert the unique ID of the components, or is the component ID the "barcode" field? Just confused about which form fields are the same for all INSERT's and what values you need to INSERT. – SOS Apr 14 '18 at 00:10
  • If you're naming your form field with brackets, CF will see that as an array - hence the 'complex' warning when trying to evaluate it as a string. – Jules Apr 15 '18 at 20:19
  • 1
    No, it won't. CF treats form fields as an array only when '*"..the form fields have the **same** name..*' AND the `this.sameFormFieldsAsArray` setting is enabled for the Application. Like the error message says, the cause is that deprecated ParameterExists() doesn't support that kind of dynamic variable reference. – SOS Apr 16 '18 at 12:40

2 Answers2

2

To answer the question, there are several ColdFusion functions that won't allow you to create a dynamic name before the function evaluates it. parameterExists() was one of those. Both isDefined() and structKeyExists() will allow dynamic variables. So will the member function of structKeyExists() > structName.keyExists("theKey").

Again, if you are new to ColdFusion, I'd simply pretend you never saw parameterExists(). I believe it has been listed as "deprecated" since CF 4.5 or somewhere around there. That's almost 20 years ago. That function has actually become somewhat of a joke about how Adobe never really throws away their trash.

As I pointed out above, I'd get rid of it completely and go with structKeyExists(). I also don't know what your whole page is doing, but with the code you provided, I'd change it to something like this:

<cfloop from="1" to="#url.loopcounter#" index="loopvar">
    <cfoutput>
    <cfif structKeyExists(form,"Component#loopvar#")>

    <!--- SANITIZE INPUTS --->
    <cfset inMontplastBarcode = sanitizingFunction(FORM.MontaplastBarcode)>
    <cfset inBadgeNumber = sanitizingFunction(URL.BadgeNumber)>
    <!--- Now use sanitized inputs in query with queryparams --->
    <cfquery name="InsertStuff" datasource="myds">
            INSERT INTO Results (MP_Barcode, Reworked, Reworked_By)
            VALUES (
              <cfqueryparam value="#inMontaplastBarcode#" cfsqltype="cf_sql_varchar" maxlength="50">
            , 'YES'
            , <cfqueryparam value="#inBadgeNumber#" cfsqltype="cf_sql_varchar" maxlength="20">
        )
    </cfquery>

    </cfif>
    </cfoutput>
</cfloop>

In your database, Reworked should be a boolean datatype. It appears that it may be a 'Yes' or 'No' string. A true boolean will be a) smaller and b) easier to validate. In the cfqueryparams, if you are using a cf_sql_varchar datatype, make sure you set an appropriate max length. You'll need to look at the available CF datatypes and see how they match up to your database datatypes. (Also see https://cfdocs.org/cfqueryparam)

For your sanitizingFunction() that you'll use to sanitize your input variables, you'll want to write a function that will follow through the steps to clean up your variables to strip out unsafe characters or other things you don't want. That is an entirely different, extremely large topic all on its own.

Shawn
  • 4,758
  • 1
  • 20
  • 29
-1

In your form, name your checkboxes simpler. Like reworked01 through reworked50.

On the action page use cfparam to default them to zero (since html forms don't post unchecked boxes):

<cfloop from="1" to="#url.loopCounter#" index="i">
    <cfparam name="form.reworked#numberFormat(i, 00)#" default="0">
</cfloop>

Then instead of fumbling with whether or not a variable exists, you can instead look for the value:

<cfloop from="1" to="#url.loopCounter#" index="i">
    <cfif evaluate("form.reworked"&i) eq 1>
        <!--- some logic here --->
    <cfelse>
        <!--- some other logic here --->
    </cfif>
</cfloop>
Jules
  • 1,941
  • 15
  • 18
  • 2
    Using `evaluate` is not really a good idea. You could just use `form["reworked"&i]` which is a cleaner way to do it. – rrk Apr 16 '18 at 07:20
  • I was staying away from brackets because of the previous confusion. Otherwise you’re right. – Jules Apr 16 '18 at 17:36