Referencing an array of queries in a cfloop - coldfusion

I am trying to reference a query from an array and use it in a cfloop tag and I keep getting an error
Error:
The expression has requested a variable or an intermediate expression result as a simple value. However, the result cannot be converted to a simple value. Simple values are strings, numbers, boolean values, and date/time values. Queries, arrays, and COM objects are examples of complex values. The most likely cause of the error is that you tried to use a complex value as a simple one. For example, you tried to use a query variable in a cfif tag.
Code:
<cfquery datasource="datasource" name="valueQuery">SELECT count FROM watermelons</cfquery>
<cfset queryArray = ArrayNew(1)>
<cfscript>
ArrayAppend(queryArray, valueQuery);
</cfscript>
<cfloop query="#queryArray[1]#">
<!---do stuff--->
</cfloop>
I have also tried (without the pounds):
<cfloop query="queryArray[1]">
<!---do stuff--->
</cfloop>
which gives this error:
The value of the attribute query, which is currently queryArray[1], is invalid.

<cfset queryIndex = queryArray[1]>
<cfloop query="queryIndex">
<!---do stuff--->
</cfloop>
'cause query="" expects a variable name.

Related

Coldfusion Complex Construct

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>

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>

conditional query in coldfusion

I need to provide some status on items in my table which I do in the last column of my table.
First I go and query one table to see if I have a confirmation for the item .
<cfquery name="focnotice" datasource="******" result="FocResult">
SELECT ecspc
FROM tbl_CNR_H
WHERE icsc = '#myarray[i].ICSC#'
AND asr_no = '#myarray[i].ASR#'
</cfquery>
The ECSPC is a field in my Table, so logic is see if there is a record. If so, see if the ECSPC value is something other then "". If so, query another table to see if there is a matching record for this ECSPC.
<cfset ISUPStatus = "#focnotice.ecspc#">
<cfif ISUPStatus NEQ "">
<cfquery name="isupStatus" datasource="******" result="ISUPResult">
select *
from tbl_ISUP
where dpc = '#ISUPStatus#'
</cfquery>
<cfset isupcount = #ISUPResult.RecordCount#>
<cfif #isupcount# GT 0>
<cfset ISUPorder = "Yes">
<cfelse>
<cfset ISUPorder = "No">
</cfif>
<cfelse>
<cfset ISUPorder = "No">
</cfif>
I get the following error in my debug
Complex object types cannot be converted to simple values.
The expression has requested a variable or an intermediate expression
result as a simple value. However, the result cannot be converted to a
simple value. Simple values are strings, numbers, boolean values, and
date/time values. Queries, arrays, and COM objects are examples of
complex values. The most likely cause of the error is that you tried
to use a complex value as a simple one. For example, you tried to use
a query variable in a cfif tag.
What am I missing here ?
You are passing invalid parameter into the Query "myarray[i].ICSC",'#myarray[i].ASR#'. You need to specify what index of array you are using.
<cfquery name="focnotice" datasource="*******" result="FocResult">
Select ecspc
From tbl_CNR_H
Where icsc = <cfqueryparam cfsqltype="cf_sql_varchar" value="#myarray[1].ICSC#">
AND
asr_no = <cfqueryparam cfsqltype="cf_sql_varchar" value="#myarray[1].ASR#">
</cfquery>
I believe the error causing you the issue lies in:
<cfset isupcount = #ISUPResult.RecordCount#>
From a quick look of your code, try using instead:
<cfset isUpCount = isUpStatus.recordCount>
But in addition please look at the comments above, especially joins.

Coldfusion Query loop works in cf10 but not 9

Why does the following work in CF10 but not CF9?
<cfset out="">
<cfif isQuery( arguments.values ) >
<cfloop query="#arguments.values#" >
<cfset out = '#out#<option value="#value#">#label#</option>'>
</cfloop>
</cfif>
CF9 states that "Complex object types cannot be converted to simple values." for the line containing the cfloop. I'm using the Coldbox framework and it's debugger information shows that arguments.values is a query with Label & Value columns.
Prior to CF10, the query attribute of cfloop can only be a string - the name of the query - not the variable itself.
So, when you put #arguments.values# it is trying to convert the complex query object to a string, to obtain a name, which is where the error comes from.
It works in CF10 because the attribute has been updated to also allow a query value.
side notes:
This line of code can be simplified:
<cfset out = '#out#<option value="#value#">#label#</option>'>
to:
<cfset out &= '<option value="#value#">#label#</option>'>
Also you very likely should be using HtmlEditFormat* on at least label, and perhaps value too.
*(or encodeForHtml if it only needs to work in CF10+)

coldfusion query loop not cooperating

I'm trying to create a function to create csv files from queries. After I run the query, I'm looping through it and appending each row's fields to a StringBuffer object. To that end, I'm putting the column names into an array:
<cfset indexes = #ListToArray(Arguments.header)# />
where the argument is currently a string like:
"col1, col2, col3...."
I've verified that both the query and the array are what they should be by dumping.
The trouble comes when looping through the query results. Given:
<cfset indexes_length = #ArrayLen(indexes)# />
<cfloop query="query_for_csv">
<cfloop index="i" from="1" to="#indexes_length#">
<cfset attr = #indexes[i]# />
<cfset sbOutput.Append(
"#query_for_csv[attr][query_for_csv.CurrentRow]#") />
</cfloop>
</cfloop>
Only the first value of the first row is output before I get the error message:
[Table (rows 10 columns col1, col2, col3):
[col1: coldfusion.sql.QueryColumn#6f731eba]
[col2: coldfusion.sql.QueryColumn#6ee67e7f]
[col3: coldfusion.sql.QueryColumn#5c6647cb]
is not indexable by col2
If I replace the variable #attr# with the literal "col2":
#query_for_csv['col2'][query_for_csv.CurrentRow]#
then the loop sails through with no problem, and spits out all the values indexed by 'col2'. Any ideas?
I would guess it's the spaces in your header list that is the problem, so probably this would work:
<cfset attr = trim(indexes[i]) />
However, since you're not using them, you probably don't need that and can just do this...
<cfloop query="QueryName">
<cfloop index="CurCol" list=#QueryName.ColumnList# >
<cfset sbOutput.Append(
QueryName[CurCol][QueryName.CurrentRow]
)/>
</cfloop>
</cfloop>
p.s.
You'll note here that there's only one pair of hashes - there only needs to be one pair in your original code snippets too (in the to attribute) - the rest are unnecessary noise.
As has already been said before, try to avoid spaces before or after a list element.
In case you want to compare notes, check out the approach Ben Nadel chose to implement such a Query2CSV converter: http://www.bennadel.com/blog/1239-Updated-Converting-A-ColdFusion-Query-To-CSV-Using-QueryToCSV-.htm