Coldfusion Complex Construct - coldfusion

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.

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.

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>

Related

ColdFusion - Reference variables in cfloop using query generated from UDF

I'm new to ColdFusion and have an interesting question regarding accessing variables inside a cfloop using a query that is generated from a query function.
I know I can create a variable, assign the result of the query function to the variable, and then loop over the variable containing the query result and access the data using the variable name given to the query attribute inside the loop as follows:
<cfscript>
q = createObject("component", "cfc.myDBquery");
result = q.myQuery();
</cfscript>
<cfloop query="result">
<cfoutput># result.MY_DATA #</cfoutput>
</cfloop>
However, consider this example:
<cfscript>
q = createObject("component", "cfc.myDBquery");
</cfscript>
<cfloop query="#q.myQuery()#">
<cfoutput># ???.MY_DATA #</cfoutput>
</cfloop>
Other than just outputting the data using the column name from the query (e.g. MY_DATA), how would I go about referencing this specific query when outputting data in the loop?
FWIW, Adobe shows this type of scenario in their documentation, however fails to show outputting data inside of the loop using this method:
https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-tags/tags-j-l/cfloop-looping-over-a-query.html
I know I'm being a bit neurotic for trying to eliminate one line from my code, I'm just curious if this is even possible while adhering to best practices.
This is a long formatted comment. Here:
<cfscript>
q = createObject("component", "cfc.myDBquery");
result = q.myQuery();
</cfscript>
Creating the object makes the myQuery() function available. It doesn't actually run it. You might be able to do this:
result = createObject("component", "cfc.myDBquery").myQuery();
Next, since you asked about best practices, don't do this:
<cfloop query="result">
<cfoutput># result.MY_DATA #</cfoutput>
</cfloop>
You are executing the cfoutput tag each time through the loop. Instead, do this:
<cfoutput>
<cfloop query="result">
#result.MY_DATA #
</cfloop>
</cfoutput>
or this
<cfoutput query="result">
#MY_DATA#
</cfoutput>
It behaves like a loop. Other comments about best practices are simply opinions. One of mine is that readable code is good code.
I believe there are 2 possibilities. First, ColdFusion doesn't require the scope when looping over a query so you could just reference the field name you need from the query like so:
<cfloop query="#q.myQuery()#">
<cfoutput>#MY_DATA#</cfoutput>
</cfloop>
Knowing non-scoped variables cause confusion and anger, I believe you can reference the name of the original query from your function call. For instance, if your 'myQuery()' function is something like:
<cffunction name="myQuery">
<cfquery datasource="myDS" name="myQry">
SELECT * FROM Names
</cfquery>
<cfreturn myQry>
</cffunction>
Then your can reference 'myQry' like so:
<cfloop query="#q.myQuery()#">
<cfoutput>#myQry.MY_DATA#</cfoutput>
</cfloop>

Use FindNoCase to find url string

I've search around and find some valuable info. about FindNoCase but I have not yet found the answer to this specific question.
I'm using FindNoCase to find "/us/" in the url and then process some code. This works fine for one country site.
<cfif FindNoCase("/us/",#cgi.SCRIPT_NAME#)>
Process some code here.
</cfif>
My question is: Is there a way to find the piece of the url, "/xx/", for multiple country sites and process the same code between the cfif tags? For example "/us/", "/ca/", "/mx/", etc.
Hopes this makes sense.
If searching for multiple different codes among a string, I'd use REFindNoCase. Make a regex like so (/us/)|(/ca/) which would look for /us/ or /ca/ and per the documentation (http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7e99.html) you can return the sub-expressions and reference them.
No looping required.
REFindNoCase("(/us/)|(/ca/)", URL, 1, true)
To reference the strings you would do:
<cfset URL = 'domaim.com/page/us/' />
<cfset match = REFindNoCase("(/us/)|(/ca/)", URL, 1, true) />
<cfif arrayLen(match) GT 0>
<cfset value = Mid(URL,match.pos[1],match.len[1]) />
<cfswitch expression="#value#">
<cfcase value="/us/">
<!--- Do something for US match --->
</cfcase>
<cfcase value="/ca/">
<!--- Do something for CA match --->
</cfcase>
<!--- ETC --->
</cfswitch>
<cfelse>
<!--- Do something if no match found --->
</cfif>
In this case, value would equal /us/. Or it should anyway. I'm writing all of this here and not actually testing on my server. You'd have to adjust this if you want to cover multiple matches in a string.
If I'm understanding your question correctly, you want to process the SAME code for several countries, if detected in the URL. You could just create a database of country codes you want to process a certain way (CountryTableXYZ), query it, then loop through that query so it'll search the URL for each entry you have in the table.
<cfloop query="CountryQuery">
<cfif FindNoCase("#countryCode#",#cgi.SCRIPT_NAME#)>
Process some code here.
</cfif>
</cfloop>
At least that's how I interpret it. If I'm not correct please let me know or clarify your initial post.

Check values in a list are all identical

Ok here's a thing, I have a form which when submitted can only update a table when a particular checkbox form variable values are identical, if one is different then it should proceed to update the table. So basically the user is selecting different rows and hitting the submit button.
So for example a good submission would be
form.claimid = 12,12,12,12,12
a bad submission would be
form.claimid = 1,5,77,33,4,
I'm not sure how to check in a list if all the values in the form.claimid list are identical or not?
I would appreciate any ideas on this.
You can also use CF's native ListRemoveDuplicates() if the version is sufficient (CF10, Railo/Lucee 4)
https://wikidocs.adobe.com/wiki/display/coldfusionen/ListRemoveDuplicates
<cfif ListLen(ListRemoveDuplicates(mylist)) eq 1>
I'll leave this as an alternate means for older CFs.
I'm not sure how to check in a list if all the values in the form.claimid list are identical or not?
There are a couple ways, most of which involve looping, but I'm going to show you a regular expression that can do this. We're going to get the first value using ListFirst, and use a ReReplace on that value to see if every other value matches.
I use a loop here but only as a demonstration.
<cfoutput>
<cfset mylist = "11,22,33,44|44,44,33,44|557,557,557">
<cfloop list="#mylist#" index="m" delimiters="|">
<cfset matchele = listfirst(m)>
<cfset eradicate = rereplace(m,"(?:(?:^|,)#matchele#(?=,|$))+","","ALL")>
"#m#", "#matchele#", "#eradicate#"<br />
</cfloop>
</cfoutput>
If you're stuck on CF 9 or lower, you could loop over the list and put the elements into a struct:
<cfset list_coll = structNew() />
<cfloop list="#mylist#" index="myitem">
<cfset list_coll[myitem] = "dummy" />
</cfloop>
<cfif structCount(list_coll) EQ 1>
<!--- This is good --->
<cfelse>
<!--- This is bad --->
</cfif>
The reason this works is that structs can't have duplicate keys.

Building a query param dynamically

Hi I am trying to build a query parameter dynamically, and am getting an error, i am using the code below,
<cfset featQuery="">
<cfloop list="#arguments.uid_features#" index="x">
<cfif x neq "0">
<cfif Len(featQuery) gt 0>
<cfset featQuery = featQuery& " AND ">
</cfif>
<cfset featQuery = featQuery & 'uid_prodf_featid = <cfqueryparam cfsqltype="CF_SQL_INTEGER" value="' & x & '">'>
</cfif>
</cfloop>
I get this error message from coldfusion;
[Macromedia][SQLServer JDBC Driver][SQLServer]Incorrect syntax near '<'.
If i look at the output, it looks correct, but normaly using cfquerypram, you just get (param1), uid_prodf_featid=(param1) in the error message it displays the following;
uid_prodf_featid = <cfqueryparam cfsqltype="CF_SQL_INTEGER" value="5">
Jason
You can't really build and execute CFML dynamically like you're attempting to do. It looks to me like you're trying to build a SQL query outside of a cfquery tag context; this would be fine, except for your need to parameterize it. If possible, change your code to run within a cfquery tag pair:
<cfquery...>
SELECT * FROM tableFoo
<cfif ListLen(arguments.uid_features)>
WHERE uid_prodf_featid IN (<cfqueryparam value="#arguments.uid_features#" list="true" cfsqltype="CF_SQL_INTEGER">)
</cfif>
</cfquery>
Also, as you can see I've changed your query structure a bit - you had a lot of code to do something that is much more easily accomplished as I show above.
edit
I see that you actually are doing AND operations with each item in your uid_features list... I have a hard time imagining there being a valid logical reason for that (rather than OR), but if so, my example won't work for that - instead change it back to a series of AND conditions within the loop.

Using coldfusion.sql.QueryTable.next() in Coldfusion 9

I am trying to write a custom tag that will iterate over a cfquery object in a special way. I found this page: http://www.zrinity.com/developers/mx/undocumentation/query.cfm outlining how to use the underlying java methods to navigate the result set, but it doesn't seem to be working in CF9.
I can call .next(), .previous(), .first(), and .last() just fine, and each method updates query.currentRow, but referencing query.columnName always returns the value from the first row, not currentRow.
Example:
<cfquery name="testQuery" datasource="source">
SELECT FooName FROM NumberedFoos
</cfquery>
<cfloop from="1" to="3" index="i">
<cfoutput>#testQuery.currentRow# => #testQuery.fooName#</cfoutput><br />
<cfset testQuery.next()>
</cfloop>
Produces:
1 => Foo 1
2 => Foo 1
3 => Foo 1
I know i could use something like testQuery.fooName[testQuery.currentRow], but that is pretty undesirable for the people I am making the custom tag for. Was the functionality described in the above link removed from CF9? If so is there an alternative?
EDIT
To expand on the why, the client wants a custom tag that allows them to "assert" certain things about a query. The client has a pretty low level understanding of CF, but are pretty solid writing SQL. Their desired end result is something akin to:
<cfquery name="purchaseTotals">
SELECT PurchaseId, Total FROM Purchases
</cfquery>
<CF_ASSERT query="purchaseTotals">
purchaseTotals.Total gte 0
</CF_ASSERT>
The desired output would be a html table with each row being the row from the query that fails the assertion. So to me, the CF_ASSERT tag need to be able to update the current row.
Edit 2:
The main challenge is to allow html in the body of the tag, while still having query values substituted from the appropriate row:
<CF_ASSERT query="purchaseTotals">
<CF_CONDITION expression="purchaseTotals.Total gte 0">
<!---error message when expression is false--->
<cfoutput>
Purchase #purchaseTotals.purchaseId# has a negative total!
</cfoutput>
</CF_CONDITION>
<CF_CONDITION expression="purchaseTotals.Total eq ''">
#PurchaseTotals.purchaseId# has a null total, this may be caused by:
<ul>
<li>Edge Case 1</li>
<li>Edge Case 2</li>
</ul>
</CF_CONDITION>
<CF_ASSERT>
The output here would be something like:
Purchase 120 has a negative total!
Purchase 157 has a negative total!
Purchase 157 has a null total, this may be caused by:
Edge Case 1Edge Case 2
Was the functionality described in the above link removed from CF9?
The internal stuff has definitely changed since the article was written in 2006. But I suspect the exact functionality you are describing may not have existed in any mx version. A key difference between your code and the linked examples is the usage of <cfoutput query=".."> (not just a plain <cfoutput>). The query attribute obviously provides some extra context when evaluating the variables. Remove it (like in your example) and the results are "the value from the first row, not currentRow.". Even under MX6, which does not bode well for subsequent versions. That exact functionality probably was not removed. It just never worked to begin with.
If so is there an alternative?
Like I said earlier, the cleanest approach would be to use array notion ie #query.column[row]#. Given that you seem to have rejected that option, you are basically left with evaluate(). You would need to loop through the query within the parent tag. Then use evaluate to process the subtag expression and content. It is not particularly elegant or simple IMO. But I think that may be good as it gets without array notation, or a ritual sacrifice of some kind.
ASSERT.cfm
<cfparam name="attributes.query" type="string">
<cfif thisTag.ExecutionMode is 'start'>
<!--- validate attributes.query is a query object --->
<cfif not ( structKeyExists(caller, attributes.query) AND IsQuery(caller[attributes.query]) )>
<cfthrow message="Attributes.query [#attributes.query#] is undefined or not a query object">
</cfif>
</cfif>
<cfif thisTag.ExecutionMode is 'end'>
<cfset variables[attributes.query] = caller[attributes.query]>
<cfloop query="variables.#attributes.query#">
<cfloop array="#thisTag.assocAttribs#" index="subTag">
<cfset variables.matchFound = evaluate(subTag.expression)>
<cfif variables.matchFound>
<cfoutput>[#currentRow#] #evaluate(DE(subTag.Content))#</cfoutput><hr>
</cfif>
</cfloop>
</cfloop>
</cfif>
CONDITION.cfm
Note: Do NOT use <cfoutput> tags within the tag content.
<cfparam name="attributes.expression" type="string">
<cfif thisTag.ExecutionMode is "start">
<cfassociate baseTag="CF_ASSERT">
</cfif>
<cfif thisTag.ExecutionMode is "end">
<cfset attributes.content = thisTag.GeneratedContent>
<cfset thisTag.GeneratedContent = "">
</cfif>
client has a pretty low level understanding of CF, but are pretty
solid writing SQL
Having said all that, are things being implemented this way because it is the best approach or because it is most similar to writing SQL ie comfortable ?
Classic example of the inner platform effect.
I would advise you not to do this as you are trying to create a system which mimics built in functionality of the base or running system which ultimately becomes a poorly implemented version of the system in which it is running on / implemented in.
Sounds confusing, I know - but this is a well known anti-pattern to avoid.
See here for more information : http://en.wikipedia.org/wiki/Inner-platform_effect
P.s. what you are looking for (although I disagree with the implementation) is itteration of a query outside of a cfloop, simply use array syntax :
#queryName.fieldName[rowNumber]#
Using this you can itterate the query however you wish, certainly no need for underlying java. Notice we aren't using queryName.currentRow. For previous() next() functionality, you just change the rowNumber up or down.