Problem converting WriteOutput to usable CFOutput Variables - cfml

I am pretty sure I have done something similar before when creating dynamic reports, but this has had me stumped. I have a set of variable names that are being dynamically created, so I am performing a WriteOutput nested within a cfoutput
<cfoutput query="GetData">
<cfscript>
writeoutput(#variabnas#);
</cfscript>
</cfoutput>
But all this does is repeats the variable names for the number of records returned by the query, rather than the data.
#FieldValue1# #FieldValue2# #FieldValue1# #FieldValue2# #FieldValue1# #FieldValue2#
Do I need to wrap the WriteOut in anything else to identify it as an actual variable? Any help gratefully received, as I don't support CF that often.

To get dynamic driven variable names work, I would make use of the evaluate() function. I also wouldn't use cfoutput and writedump with cfscript at the same time. This should work:
<cfoutput>
<cfloop query="GetData">
#evaluate("#variabnas#")#
</cfloop>
</cfoutput>
But please use evaluate() with caution because of security and overhead. For further information please read this.

Related

Dynamic Freemarker variable name

I'm trying to set a variable with a dynamic name. This means the name for my new variable comes from another variable:
<#-- in real world I wouldn't declare this variables right here -
they would come from somewhere else -->
<#assign varName = "myVarName"/>
<#assign varValue = "myVarValue/>
<#... set the variable .../>
So that the value can be referenced as follows:
${myVarName} <#-- prints "myVarValue" -->
In a Java directive I would use
Environment#setVariable(String name, TemplateModel model)
to achieve this. But is there a possibility to achieve this with Freemarker directly?
I had a similar problem and Special Variable Reference page helped me:
vars: Expression .vars.foo returns the same variable as expression foo. It's useful if for some reasons you have to use square bracket syntax, since that works only for hash sub variables, so you need an artificial parent hash. For example, to read a top-level variable that has a strange name that would confuse FreeMarker, you can write .vars["A strange name!"]. Or, to access a top-level variable with dynamic name given with variable varName you can write .vars[varName]. Note that the hash returned by .vars does not support ?keys and ?values.
In my case I had to use only strings in my model. I had a bunch of names like Name1, Name2, ... Name10. To make a table of these names I used this code:
<#list 1..10 as x>
<#if .vars["Name" + x]??>
<tr>
<td align="center">${.vars["Name" + x]}</td>
</tr>
</#if>
</#list>
Use a hash. That is, use the name of the variable as the key of the hash.
There's no directive that assigns to a variable that has a dynamic name. But here's a hack to achieve that:
<#'<#assign ${varName} = varValue>'?interpret />
This isn't terribly fast though. It involves FTL parsing each time it's evaluated.
I guess you can do it like this:
${myVarName?eval} <#-- prints "myVarValue" -->
I found the answer from here, and it works to me.

ArraySum only a certain rows in the cfoutput of a query

I am trying to add only certain rows to this output. Currently this output adds all rows, which is used for a Total row at the very end of
<cfoutput query="qrySummary">
#numberFormat(ArraySum(ListToArray(ValueList(qrySummary.secThreeCount))), ",")#
This obviously totals all of SecThreeCount values, but what if I want to exclude the last 2 rows from that list??
Is that possible in coldfusion?
(If this makes a difference)>> So there are 13 rows returning, I want the first 11 rows and exclude the last 2.
I know that I can limit the SQL return of that query to exclude those 2 rows, but I wanted less code to write and keep things neat. And also learn if it is possible:)
Thanks in advance.
Well I think if you don't need those two rows, you should not be returning them in the first place. That would be the best answer. From your statement "but I wanted less code to write" you're optimising in the wrong place: don't optimise for yourself, optimise for the solution.
Leigh's come in underneath me as I've been testing the code for this, but here's a proof of concept using subList():
numbers = queryNew("");
queryAddColumn(numbers, "id", "integer", [1,2,3,4,5,6]);
queryAddColumn(numbers, "maori", "varchar", ["tahi", "rua", "toru", "wha", "rima", "ono"]);
maori = listToArray(valueList(numbers.maori));
subset = maori.subList(2,5);
writeDump([numbers, subset]);
This returns an array with elements ["toru","wha","rima"].
If you are running CF10, one option is using ArraySlice. Grab only the first eleven elements, then apply arraySum.
<cfset allValues = ListToArray(ValueList(qrySummary.secThreeCount))>
<cfset subTotal = arraySum( arraySlice(allValues, 1, 11))>
For earlier versions, there is the undocumented subList(...) approach. It takes advantage of the fact that CF arrays are java.util.List objects under the hood, and uses List.subList(..) to grab a subset of the array.
Another approach. You don't want to add up the values for the last 2 elements, so remove them from your array:
<cfset values = ListToArray(ValueList(qrySummary.secThreeCount))>
<!--- delete the last element --->
<cfset arrayDeleteAt(values, arrayLen(values))>
<!--- delete the last element again --->
<cfset arrayDeleteAt(values, arrayLen(values))>
#numberFormat(ArraySum(values), ",")#
Alternatively, given that you're looping over the query anyway, you could simply add the totals up as you go (with a tiny bit of logic to not bother if you're on the last or penultimate row)

Coldfusion dynamic query output

Not sure if this is possible or not. What I am trying to do is build an output string via queries. I am concatenating the output "name" and appending the "value" to the end. Then outputting the string. I don't think this is possible. But I am looking for any alternatives.
So this is what I have:
qry1 is the main query. qry2 gets the value to append to the end of the string.
So the value of variable test would look like this: "variables.qry1.100"
Which would make sense to qry1 as this is part of the query object. So then this string would return a correct value from the database as there is a subquery called 100
<cfoutput>
<cfloop query="variables.qry2">
<cfset test = variables.qry1. & variables.qry2.#valueID#>
<td>#test#</td>
</cfloop>
</cfoutput>
Many thanks.
JC
So basically - given your example - you have a column in qry1 called 100 (etc, perhaps also 200, 300 etc), and the values 100, 200, 300 etc are row values in the valueID column of qry2? And qry1 is a single-row query? Is that right?
If you have the name of the column in a dynamic string, you use this syntax:
queryName[columnName][rowNumber]
Where queryName is the variable that is the query, columnName is the string holding the column name, and rowNumber is the row number (either an actual number, or a variable containing one).
So using your example variables, you code would be:
<td>#variables.qry1[variables.qry2.valueID][1]#</td>
There is no need to use evaluate() to do this, and it is not a good solution here. There is very seldom a need to use evaluate() in CFML, since the days of CF5.
I have no idea of the background of how your data structures came to be the way they are, but if you are needing to write the sort of code you are suggesting you need to... I'd be looking long and hard at how you're going about things.
What your trying to do is possible, but you need to build a variable name first.
Instead of
<cfset test = variables.qry1. & variables.qry2.#valueID#>
Try
<cfset test = "variables.qry1.#variables.qry2.valueID#">
Test will then be variables.qry1.[valueID value]. Note that [valueID value] is what is getting returned from the query, so the actual value inside the variable.
Then to display the value of variables.qry1.[valueID value].
#evaluate(test)#
UPDATE As stated by Adam Cameron's answer. You should really try to avoid the evaluate() function, it's quite a performance hit and not considered good practice. Instead rather use the following code (This is copied from Adam Cameron's answer)
#variables.qry1[variables.qry2.valueID][1]#
NOTE: Go look at Adam Cameron's answer for a better description of whats going on.
Adam has the correct solution. Here is a modified version of your original code that does what I think you are trying to do.
<cfoutput query="variables.qry1">
<tr>
<cfloop query="variables.qry2">
<cfset test = variables.qry1[variables.qry2.valueID][variables.qry1.currentrow]>
<td>#test#</td>
</cfloop>
</tr>
</cfoutput>

Why a parameter is not defined, if I can dump it and it has a value in Coldfusion?

Just a little clueless... using Coldfusion8, if I dump my session to file:
<cfdump output="D:\ColdFusion8\logs\dump.txt" var="#Session#">
this includes:
accounttyp: whatever
I get the same result if I only dump this parameter:
<cfdump output="D:\ColdFusion8\logs\dump.txt" var="#Session.accounttyp#">
Question:
If it's defined and dump-able, how come checking isDefined like so:
<cfdump output="D:\ColdFusion8\logs\dump.txt" var="#IsDefined(Session.accounttyp)#">
turns out to be NO? If it's there it should be defined, shouldn't it?
Thanks for some clarification.
<cfdump output="D:\ColdFusion8\logs\dump.txt"
var="#IsDefined(Session.accounttyp)#">
It is because the syntax is incorrect. IsDefined expects the name of a variable ie a string. By omitting the quotes around the variable name, the session variable gets evaluated first, and its value ("whatever") is what gets passed into IsDefined. So the code is actually checking for a variable named "whatever", not "session.accounttyp" ie:
<cfif IsDefined("whatever")>
That is why the result is NO. This is the correct syntax. (Notice the quotes and lack of pound signs).
<cfif IsDefined("Session.accounttyp")>
However, I would suggest switching to structKeyExists. It is generally preferred over IsDefined because it is more precise.
This returns a boolean value:
#IsDefined(Session.accounttyp)#
So, you are asking it to return yes or no.
A better test might be this:
<cfif isDefined("Session.accounttyp")>
<cfdump output="D:\ColdFusion8\logs\dump.txt" var="#Session.accounttyp#">
</cfif>

Execute coldfusion code stored in a string dynamically?

I have an email body stored as a string in a database, something like this:
This is an email body containing lots of different variables. Dear #name#, <br/> Please contact #representativeName# for further details.
I pull this field from the database using a stored proc, and then I want to evaluate it on the coldfusion side, so that instead of "#name#", it will insert the value of the name variable.
I've tried using evaluate, but that only seems to work if there's just a variable name. It throws an error because of the other text.
(I can't just use placeholders and a find/replace like this - Resolving variables inside a Coldfusion string, because the whole point of storing this in a database is that the variables used to build the string are dynamic. For example, in one case the name field can be called "name" and in another it could be "firstName", etc.)
I would loop over each #variableName# reference and replace it with the evaluated version.
A regex will be able to find them all and then a loop to go over them all and just evaluate them one by one.
You need to write it to a file and CFINCLUDE it. This will incur a compilation overhead, but that's unavoidable.
Can you not save the code to the file system and just store a reference to where it is in the DB? That way it'll only get recompiled when it changes, rather than every time you come to use it?
<!--- pseudo code --->
<cfquery name="q">
SELECT fileContent // [etc]
</cfquery>
<cfset fileWrite(expandPath("/path/to/file/to/write/code.cfm"), q.fileContent)>
<cfinclude template="/path/to/file/to/write/code.cfm">
<cfset fileDelete(expandPath("/path/to/file/to/write/code.cfm"))>
That's the basic idea: get the code, write the code, include the code, delete the code. Although you'll want to make sure the file that gets created doesn't collide with any other file (use a UUID as the file name, or something, as per someone else's suggestion).
You're also gonna want to load test this. I doubt it'll perform very well. Others have suggested using the virtual file system, but I am not so sure there'll be much of a gain there: it's the compilation process that takes the time, not the actual file ops. But it's worth investigating.
Are you using ColdFusion 9 or Railo? If yes, writing to and including in-memory files may be quick and simple solution. Just generate file name with something like CreateUUID() to avoid collisions.
So basically, after researching and reading the answers, it seems that these are my options:
Have separate fields in the table for each variable and evaluate them individually. e.g. nameVariable, reprNameVariable, and then I can build the body with code like this:
This is an email body containing lots of different variables. Dear #evaluate(nameVariable)#, <br/> Please contact #evaluate(reprNameVariable)# for further details.
Have an "emailBody" field with all the text and applicable field names, and write it to a temporary file and cfinclude it. (As suggested by Adam Cameron, and I think that's what Sergii's getting at)
Have an "emailBody" field and write code to loop over it, find all coldfusion variables, and replace them with their "evaluate"d version. (As suggested by Dale Fraser)
Have small template files, one for each report type, with the email body, and have a field "emailBodyTemplate" that indicates which template to include. (As suggested by Adam Cameron)
Now I just have to decide which to use :) When I do, I'll accept the answer of the person who suggested that method (unless it's one that wasn't suggested, in which case I'll probably accept this, or if someone comes up with another method that makes more sense)
It's been a while since you posted this - but it's EXACTLY what I do. I found your question while looking for something else.
I've simply created my own simple syntax for variables when I write my emails into the database:
Hello ~FirstName~ ~LastName~,
Then, in my sending cfm file, I pull the email text from the database, and save it to a variable:
<cfset EmailBody = mydatabasequery.HTMLBody>
Then I quickly strip my own syntax with my variables (from another query called RecipientList):
<cfset EmailBody = ReplaceNoCase(EmailBody, "~FirstName~", "#RecipientList.First#", "ALL")>
<cfset EmailBody = ReplaceNoCase(EmailBody, "~LastName~", "#RecipientList.Last#", "ALL")>
Then I simply send my email:
<cfmail ....>#EmailBody#</cfmail>
I hope you manage to see this. If you control the authoring of the email messages, which I suspect you do, this should work well.
Russell