Trying to replace all evaluate functions with dynamic notation in ColdFusion 9 - coldfusion

Hello I have been tasked with replacing all evaluate() functions in this coldfusion app. I am having some issues with some fairly complex evaluations and I just can't figure it ou.
When it is a simple change like
This:
<cfif isNumeric(evaluate("form.value_#REPORT_FIELD_ID#"))>
To This:
<cfif isNumeric(form["value_" & REPORT_FIELD_ID])>
It's all good but for this:
<cfif evaluate("qrySearch.#qryReportFields.FIELD_NAME[qryReportFields.currentRow]#") eq true>
Not so much
I have tried several things. I thought this would work
<cfif qrySearch[#qryReportFields.FIELD_NAME[qryReportFields.currentRow]#] eq true>
But it throws a Complex object types cannot be converted to simple values. error.
Any help would be greatly appreciated.

I believe if you use bracket notation for query column name you have to also include row number.
<cfif qrySearch[qryReportFields.FIELD_NAME[qryReportFields.currentRow]][qrySearch.currentrow] eq true>
Or just pass 1 instead of currentrow if only one record, not in loop, etc

Related

Using ColdFusion, how do I replicate a compare statement with listFind?

I'm setting up something to allow me to masquerade as another user in my system. But, I want to use listFind instead of a compare() method.
<cfif compare(session.userName, "userOne") EQ 0>
<cfset #session.userName# = "userThree">
</cfif>
In the above statement, I'm trying to conform it to a listFind, where if userOne is currently logged in, then set the session.userName to userThree. But, I'm having trouble.
What I have thus far...
<cfif #ListFind("userOne, UserTwo")#>
<cfset #session.userName# = "userThree">
</cfif>
You will need to provide two independent arguments to ListFind. At the moment you are providing one string. The list is also the first arguments and string search for is the second.
<cfif ListFind("userOne", session.userName)>
<cfset session.userName = "userThree">
</cfif>
On a side note, the hashes are only necessary for string interpolation. In other words, only when you want a variable/expression to be evaluated and inserted into a string.

Nullif - using in Coldfusion

Looking for a little help with this code. Without posting the entire file which is way to big I just need a little help with using Nullif in Coldfusion.
I could I guess use it in my SQL statment, but for the sake of learning I am wondering if it can be used when setting variables as follows :-
The code doesn't throw any errors but I'd like to know where I would place the 0 after the Nullif.
<cfif AE_C NEQ 0>
<cfset AE_P=AE_T/AE_C>
<cfset AE_A=AE/AE_C*100>
<cfset AE_B = AE-AE_C/H8*H9>
<cfset AE_D=AE/H9*H8>
<cfelse>
<cfset AE_P=ISNULL(AE_T/NULLIF(AE_C))>
<cfset AE_A=ISNULL(AE/NULLIF(AE_C*100))>
<cfset AE_B=ISNULL(AE-AE_C/NULLIF(H8*H9))>
<cfset AE_D=ISNULL(AE/NULLIF(H9*H8))>
</cfif>
Hoping it can be done this way.
IMPORTANT: Your code is not showing any error because ISNULL is masking the error.
Also NULLIF is not a valid ColdFusion function. I believe the reason why there is no error in your page because, ColdFusion ISNULL() function seems to be a very versatile one and showing some undocumented characteristics. ISNULL() does not return an error even if the expression inside it is defined or not if the expression is syntactically valid.
eg.
ISNULL(AE_T/NULLIF(AE_C)) // No error because AE_T/NULLIF(AE_C) is a valid statement.
What you could do as an alternative is the following.
The following is a bit hacky, but you can check out the function val(). It will return 0 for any string that is not a number (check the doc for more details).
NULLIF(AE_C) becomes val(AE_C).
<cfset AE_P=ISNULL(AE_T/val(AE_C))>
Still if the val() return 0, then the output of ISNULL() will be YES, because division by 0 throws error.
This is some code that was written by Ben Nadel that I've found to resolve the error, perhaps someone can assist me in how I would implement it as I just can't get my head around it.
<!---
Do SQL division with divide-by-zero protection. But this,
time, let's provide a default value if the division is
not valid.
--->
<cfquery name="qDivision" datasource="#REQUEST.DSN.Source#">
SELECT
(
ISNULL(
(45 / NULLIF( 0, 0 )),
0
)
) AS value
;
</cfquery>
<!--- Output resulting value. --->
[ #qDivision.value# ]
This is more a comment than an answer. See RRK's answer about NULLIF not being a valid ColdFusion function and ISNULL() hiding an error.
My comment is more about the logic of your operation. You don't need to do some of the steps in your <CFELSE>
Original:
<cfif AE_C NEQ 0>
<cfset AE_P=AE_T/AE_C>
<cfset AE_A=AE/AE_C*100>
<cfset AE_B = AE-AE_C/H8*H9>
<cfset AE_D=AE/H9*H8>
<cfelse>
<cfset AE_P=ISNULL(AE_T/NULLIF(AE_C))>
<cfset AE_A=ISNULL(AE/NULLIF(AE_C*100))>
<cfset AE_B=ISNULL(AE-AE_C/NULLIF(H8*H9))>
<cfset AE_D=ISNULL(AE/NULLIF(H9*H8))>
</cfif>
I'm assuming you're using the NULLIF() to prevent divide by zero error.
First, in the languages I'm familiar with, NULLIF() requires two arguments: NULLIF(a,b) so that if a is not equal to b, it will return a, otherwise it will return NULL.
That said, your cfif says that if AE_C is not 0 then do x, else do y. I'm assuming it should be <cfset AE_P=ISNULL(AE_T/NULLIF(AE_C,0))>. If you extrapolate that out, you'd get AE_T/NULLIF(0,0) == AE_T/NULL == NULL. So you can just short-circuit that block and just set AE_P=NULL. But you also have the ISNULL() function around that, so that will always be true also. And ISNULL() (except in ColdFusion) also takes 2 arguments. In CF, it returns TRUE/FALSE (or YES/NO to be pedantic). So now you've essentially done <cfset AE_P = YES/TRUE>. I'm guessing that wasn't your original intent. So ISNULL() probably isn't appropriate here. In SQL, ISNULL(a,b) would mean that if a is NULL, return b. So (in SQL) you could essentially do AE_P = ISNULL(x,0) and that would mean that if AE_C was 0, rather than getting the divide by zero error, you could just set AE_P = 0.
TLDR; There isn't a NULLIF() function in ColdFusion, so if you're trying to prevent the div0 errors, you'll have to either do it in your SQL or modify your ColdFusion sets:
<cfif AE_C NEQ 0>
<cfset AE_P=AE_T/AE_C>
<cfset AE_A=AE/AE_C*100>
<cfelse>
<cfset AE_P=0>
<cfset AE_A=0>
</cfif>
I don't know the flow of your code, so I don't know how H8 and H9 are affected by AE_C, so you may have to check those for 0 also.
But, I'd still go back to my usual belief that if these calculations are used in SQL and not really in code, then they should be done in SQL rather than being passed out to the app server and then back to the SQL. Again, though, I don't know what your code is doing, so it may be more appropriate to have those calcs in code.

CF9: What is this evaluate statement evaluating?

I'm stuck and need a fresh set of eyes on this, please.
I'm working with someone else's spaghetti code who is no longer around and having a heck of a time figuring out what they were evaluating.
<cfset surveyCount = 0>
<cfloop query="surveys">
<cfif evaluate("defaultReport" & ID)>
<cfset surveyCount = surveyCount + 1>
</cfif>
</cfloop>
In the query dump, I see 9 records which is what I am expecting but because because the evaluate is failing, the surveyCount isn't being incremented. I do not see any columns for defaultReport. In my 15 years of working with CF, I've always avoided evaluate() and now when I need to analyze it, I'm at a complete loss. Can someone offer any guidance?
Added CFDump image (some columns names have been removed for privacy and security):
UPDATE I:
This file has numerous cfinclude statements and very little code formatting. As a result, I overlooked some cfinclude statements. I found the following. I'm still looking but wanted to document this as I dig.
<cfloop query="surveys">
<cfscript>
variables["defaultReport" & ID] = evaluate(thisAssociation & "Price");
</cfscript>
</cfloop>
UPDATE II:
Dumping the variable scope, I did confirm the variable I am looking for (finding the query I posted in UPDATE I helped, too). :)
What they wanted to do is to increase surveyCount but only if this thing: evaluate("defaultReport" & ID) evaluates to true.
From your query dump picture it looks like the IDs are numbers like 144, 145, etc...
In this context, you can think at evaluate("defaultReport" & ID) as something like defaultReport144, defaultReport145, etc... (these are variables set somewhere in the code).
So the code:
<cfif evaluate("defaultReport" & ID)>
<cfset surveyCount = surveyCount + 1>
</cfif>
becomes (for an ID of 144, the first one on your query loop)
<cfif defaultReport144>
<cfset surveyCount = surveyCount + 1>
</cfif>
and so on... for the other IDs
So, search your code for where variables like defaultReport144, defaultReport145, etc... are set to either true or false (0 or 1).
Something like:
<cfset defaultReport144 = true />
or maybe they use some expression that evaluates to true or false, like:
<cfset defaultReport144 = [some expression] />
If you can't find, then maybe the code was changed or removed in the place where these defaultReport... variables were set.
ColdFusion evaluate() documentation:
https://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7f4e.html
You need to look for a variable outside of your query. This variable has a name of default#ID# . It may be called.
variables.default#ID#
form.default#ID#
url.default#ID#
request.default#ID#
attributes.default#ID#
etc.
Basically ColdFusion is going to go through every scope until it finds something. (No this is not a good approach)
If you have to clean this up, I would recommend not using such an ambiguous approach. In short, there is no real way to know what it is evaluating.

How to refer to variables when the names are indefinite and must be found by regex first, using Coldfusion?

I have an html form that collects variables that look like:
ent1_label
ent2_label
ent1att1_label
ent1att2_label
ent2att1_label
ent2att2_label
ent1att1val1_label
ent1att1val2_label
...
ent2att2val2_label
I don't know in advance how many of ent, att, or val will be coming from the form to the action script. In the input form, I run loops that concatenate the variable names:
ent<cfoutput>#i#</cfoutput>att<cfoutput>#j#</cfoutput>val<cfoutput>#k#</cfoutput>_label
(where i, j, and k are the number of ent, att, and val)
This works great on the input form, but then I'm lost on how to refer to them in the action script.
I've played around with #form.fieldnames# which has all of the actual variable names.
<cfset #formfields# = listToArray(#form.fieldnames#, ",")>
<cfset #formlength# = arraylen(#formfields#)>
<cfset #entattval_label# = arrayNew(1)>
<cfloop from="1" to="#formlength#" index="i">
<cfif REfind("ENT[0-9]*ATT[0-9]*VAL[0-9]*_LABEL", #formfields[i]#) EQ 1>
<cfset arrayAppend(entattval_label, "#formfields[i]#")>
</cfif>
</cfloop>
will make separate arrays for each subset of variables I need. But how do I make it print the contents of the variables, instead of their names?
<cfset #label_length# = arraylen(#entattval_label#)>
<cfloop from="1" to="#label_length#" index="i">
<cfoutput>#entattval_label[i]#</cfoutput>
</cfloop>
You can reference a variable with a dynamic name using what's called array notation:
foo.bar can also be written as foo['bar'] - even though it's a structure and not an array.
This means that you can inject any dynamic name into your reference that you like; and this also works for built in variable scopes like form, url, etc:
For example: form['ent#i#att#j#val#k#_label'] where i, j, and k are the integers you need.
If you find that hard to read, you could also write it as:
form['ent' & i & 'att' & j & 'val' & k & '_label']
In these situations, if the form is being expanded on the client-side (e.g. with JavaScript) without server involvement, I often find it's easiest to include a numeric hidden field (or in your case, maybe 3?) to indicate the ranges for i, j, and k.
(Too long and too much formatting for an easily-readable comment.)
Just for the record, you can significantly shorten your code by using ReMatchNoCase() instead of ReFind() if you're using CF8+ or Railo.
ReMatchNoCase (and ReMatch of course) looks for as many matches as possible. I created a form.fieldnames variable for demonstration but that's not even really needed
<cfset form.fieldnames = "ent1_label,ent2_label,notvalid,btnSelect,ent1att1_label,ent1att2_label,ent2att1_label,ent2att2_label,ent1att1val1_label,ent1att1val2_label,ent2att2val2_label">
<Cfset NamesArray = REMatchNoCase("ENT[0-9]*ATT[0-9]*VAL[0-9]*_LABEL",form.fieldnames)>
<cfoutput><cfloop from="1" to="#ArrayLen(NamesArray)#" index="i">
#NamesArray[i]#: #form[NamesArray[i]]#<br />
</cfloop>
</cfoutput>

When should I use # in ColdFusion?

This has been one of the biggest obstacles in teaching new people ColdFusion.
When to use # is ambiguous at best. Since using them doesn't often create a problem it seems that most people gravitate to using them too much.
So, what are the basic rules?
I think it may be easier to say where NOT to use #. The only place is in cfif statements, and cfset statements where you are not using a variable to build a string in quotes. You would need to use the # sign in almost all other cases.
Example of where you are not going to use it:
<cfset value1 = 5>
<cfset value2 = value1/>
<cfif value1 EQ value2>
Yay!!!
</cfif>
<cfset value2 = "Four plus one is " & value1/>
Examples of where you will use #:
in a cfset where the variable is surrounded by quotes
<cfset value1 = 5>
<cfset value2 = "Four plus one is #value1#"/>
the bodies of cfoutput, cfmail, and cffunction (output="yes") tags
<cfoutput>#value2#</cfoutput>
<cfmail to="e#example.com" from="e#example.com" subject="x">#value2#</cfmail>
<cffunction name="func" output="yes">#value2#</cffunction>
in an attribute value of any coldfusion tag
<cfset dsn = "myDB"/>
<cfquery name="qryUsers" datasource="#dsn#">
<cfset value1 = 5>
<cfset value2 = 10/>
<cfloop from="#value1#" to="#value2#" index="i">
<cfqueryparam value="#value1#" cfsqltype="cf_sql_integer"/>
EDIT:
One oddball little thing I just noticed that seems inconsistent is conditional loops allow the variable name to be used with and without # signs.
<cfset value1 = 5>
<cfloop condition = "value1 LTE 10">
<cfoutput>#value1#</cfoutput><br>
<cfset value1 += 1>
</cfloop>
<cfset value1 = 5>
<cfloop condition = "#value1# LTE 10">
<cfoutput>#value1#</cfoutput><br>
<cfset value1 += 1>
</cfloop>
Here's what Adobe has to say about it:
Using number signs
String interpolation:
<cfset name = "Danny" />
<cfset greeting = "Hello, #name#!" />
<!--- greeting is set to: "Hello, Danny!" --->
Auto-escaped string interpolation in cfquery:
<cfset username = "dannyo'doule" ?>
<cfquery ...>
select u.[ID]
from [User] u
where u.[Username] = '#username#'
</cfquery>
<!--- the query is sent to the server (auto-escaped) as: --->
<!--- select u.[ID] from [User] u where u.[Username] = 'dannyo''doule' --->
<!--- note that the single-quote in the username has been escaped --->
<!--- by cfquery before being sent to the database server --->
Passing complex arguments/attributes in CFML:
<cfset p = StructNew() />
<cfset p.firstName = "Danny" />
<cfset p.lastName = "Robinson" />
<cfmodule template="modules/view/person.cfm" person="#p#">
<!--- the variable Attributes.person will be --->
<!--- available in modules/view/person.cfm --->
Passing complex arguments requires # signes only in CFML, not CFScript. In addition, you can pass any kind of value: simple values, arrays, structs, cfcomponents, cffunctions, java objects, com objects, etc.
In all these cases, the text between the # signs does not have to be the name of a variable. In fact, it can by any expression. Of course, for string interpolation, the expression must evaluate to a simple value, but for argument/attribute passing in CFML, the expression may evaluate to any complex value as well.
The #...# syntax allows you to embed an expression within a string literal. ColdFusion is unfortunately pretty inconsistent about what's a string and what's an expression. Jayson provided a good list of examples of when to use (or not use) #s.
At the risk of sounding like a wise-guy, a rule of thumb is: use # around variables or expressions only when not doing so doesn't yield the correct result. Or: if you don't need them, don't use them.
I like Jayson's answer though.
Let's start by assuming you aren't talking about cfoutput tags, cause then the answer is always, elsewhere in your code, if you are inside of quotation marks, then need to use # symbols if it's possible to actually type the value that is going to be used...so if you are in a cfloop tag setting the 'to' attribute, you could easily type 6, but if you want to use a variable you need to use the # symbols. Now if you are in a cfloop tag setting the query parameter, there is no way you could actually type the query into that attribute, there is no way to type a query, so no # symbols are needed.
Likewise in a cfdump tag, you can dump static text, so if you want to dump the contents of a variable, then you will need to use a # symbol. This problem is generally self-correcting, but I feel your pain, your students are probably frustrated that there is no "ALWAYS USE THEM" or "NEVER USE THEM" approach...sadly this isn't the case, the only thing that is true, is only one way inside of quotation marks is going to be correct. So if it isn't working look at it hard and long and think to yourself: "Could I type that value out instead of using the value contained in that variable?" If the answer is no, then the # symbols won't be needed, otherwise get your # character foo on.