Group queries inside a cfloop - coldfusion

I'm looking to loop through a query, and would like to use grouping, like one would using cfoutput. I know CF10 added that support, but is there a script that emulates that behaviour so that items can be iterated easily?
Edit:
There are ways of getting around the lack of grouping in cfloop, by rearranging cfoutput tags, so they are not nested. The reason I'm looking for the cfloop workaround is that when nesting cfoutput, you need to use the results from the same query. I'd like to use my own QoQ and loop through the result.

OK, so you want to do this sort of thing:
<cfoutput query="query1">
<!--- stuff --->
<cfoutput query="query2" group="col>
<!--- more stuff --->
<cfoutput>
<!--- still more stuff --->
</cfoutput>
<!--- almost the last stuff --->
</cfoutput>
<!--- last stuff --->
</cfoutput>
?
And the second loop gives you an error:
Invalid tag nesting configuration.
A query driven cfoutput tag is nested inside a cfoutput tag that also has a query attribute. This is not allowed. Nesting these tags implies that you want to use grouped processing. However, only the top-level tag can specify the query that drives the processing.
You should be able to revise that to:
<cfloop query="query1">
<cfoutput>
<!--- stuff --->
</cfoutput>
<cfoutput query="query2" group="col>
<!--- more stuff --->
<cfoutput>
<!--- still more stuff --->
</cfoutput>
<!--- almost the last stuff --->
</cfoutput>
<cfoutput>
<!--- last stuff --->
</cfoutput>
</cfloop>
There's another option to emulate the group loop if you must. But that's a bunch of thinking and typing I'd rather avoid if poss, so let me know if this approach works first.

Related

Why is cfoutput being ignored?

I have a very simple CFML page as follows:
<cfquery name="qry" datasource="#application.db.source#" username="#application.db.user#" password="#application.db.pass#">
SELECT * FROM changemgmt.rfc WHERE rfc_id = <cfqueryparam cfsqltype="CF_SQL_INTEGER" value="0">;
</cfquery>
<cfoutput query="qry">
#qry.RecordCount#
</cfoutput>
My <cfoutput> tag is not working as I would expect. If you were to look at the source code of this page, it would be entirely composed of empty lines. It's as if the <cfoutput> tag is being parsed out by the server. However if I change the code to:
<cfoutput>
#qry.RecordCount#
</cfoutput>
I am using Lucee as my backend CFML engine. Can anyone explain to my why there is a difference?
<cfoutput query="qry"> this is used to loop throw all the rows in the query. If your query result have no rows, the execution will never reach the login with in the <cfoutput query="qry">. But if there are 1+n rows in the result, your code will print the recordcount 1+n times.
In this case if all you want is to print recordcount of the result, then you just need to use <cfoutput>#qry#</cfoutput>
But if you want to print values each row of the query then you can use <cfoutput query="qry">
<cfoutput query="qry">
<div>
<span>#qry.rfc_id#</span>
<span>#qry.name#</span>
...
</div>
</cfoutput>

ColdFusion cfloop issue

I'm running ColdFusion 2016. I have a cfloop which pulls in data from a query, all other ColdFusion queries work fine on the page and if I pull in the same variable in an output outside the loop, it works fine, except the loop is giving me an error. The error output says:
Variable GPS_LATITUDE is undefined.
Which is correct, as in the database there is no GPS_LATITUDE but there is a GPS_LATITUDE1.
I need to add the loop number on the end of the variable so as it loops pulls in the data gps_latitude1, gps_latitude2, gps_latitude3 not just gps_latitude.
My loop code is...
<cfoutput>
<cfloop index="i" from="1" to="3">
<td><p>#gps_latitude[i]#</p></td>
<td><p>#gps_longitude[i]#</p></td>
</cfloop>
</cfoutput>
Any guidance much appreciated.
#elixieru, You can't directly give gps_latitude[i]. which is not check your query which is consider it as Array. I can imagine your scenario and give my sample code about how to get an same columnName with
<cfquery name='test' datasource="mytest">
select * from test
</cfquery>
This is my sample query. It's having column name as address1, address2 etc... I'm going to get the data about address1 & address2 like your scenario.
<cfloop query="test">
<cfloop from="1" to="2" index="i">
<cfset a = test["address#i#"]>
<cfoutput> #a# </cfoutput> <br/>
</cfloop>
</cfloop>
Here I'm looping over the query and so some simple / index loop based on my count ( Address1,2,3,4,5 etc ). For now I'm just use 2 like from 1 to 2.
Here I've store the test['address#i#'] in variable a and print that variable. Now test['address#i#'] it will consider as test.address1
I hope my sample help you more.

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>

Can i remove Pagination from external site with cfhttp

I am fetching one website results in my website and if there are any much more records, the pagination is also coming along with it, so it is displaying pagination and records
this is more than a question rather than before i make any try and i do not know where to start
is this possible, can i make infinite pagination of the results through some client side or server side, let me know please thanks
Pagination look like this in the cfhttp.filecontent as:
<TD align="right" width="100"><font class="MainBody">Pages: << 1 2 >> </FONT></TD>
This should work for you. It searches for a TD tag followed by a FONT tag, followed by Pages.. and then searches to the first closing TD.
The result is stored in newfilecontent.
<cfset newfilecontent = REReplaceNoCase(cfhttp.filecontent,"<td.*?><font.*?>Pages.*?<\/td>","","ALL")>
With a more detailed question, what you need is basically a rudimentary spider.
This is only designed to work from the first page of results onward. You can't target this at say, page 3, and get page 2 and page 1's.
<cfhttp...> <!--- initial cfhttp --->
<cfset buildContents = ArrayNew(1)>
<cfset buildContents[1] = ReReplaceNoCase(cfHttp.fileContent,".*<body.*?>(.*)</body>.*","\1","ALL")>
<!--- Quick regex to parse the contents of the body tag out of the cfhttp --->
<cfloop condition="#ReFindNoCase("(http[^""]*?pagenum=\d+)(?="">>>)",currentContent)# gt 0">
<cfset GetNextPage = ReMatchNoCase("(http[^""]*?pagenum=\d+)(?="">>>)",currentContents)>
<cfhttp url="#GetNextPage[1]#"... result="inLoop">
<cfset currentContents = ReReplaceNoCase(inLoop.filecontent,".*<body.*?>(.*)</body>.*","\1","ALL")>
<cfset ArrayAppend(buildContents,REReplaceNoCase(currentContents,"<td.*?><font.*?>Pages.*?<\/td>","","ALL"))>
<cfif ArrayLen(buildContents) gt 10>
<!--- This code is untested, so this is a safety that you can remove or modify. If BuildContents has more than ten elements, it stops the looping. You can remove this cfif or maybe raise the number as a safety net.--->
<cfbreak>
</cfif>
</cfloop>
<cfdump var="#buildContents#">

How to find the nested cfoutput recordcount when using group

Consider the following:
<cfoutput query="resources" group="type">
<h4>#type#</h4>
<cfoutput>
#name#
</cfoutput>
</cfoutput>
resources.recordcount would give me the total number of records, but is there an elegant way of finding out the recordcount of the nested data? e.g
<cfoutput query="resources" group="type">
<h4>#type# (#noofrecords# of #resources.recordcount#)</h4>
<cfoutput>
#name#
</cfoutput>
</cfoutput>
I could probably do something hacky with loops, but wondered if there was a way of doing it using the cfoutput groups specifically.
You could do an output before to get the count. This would be more efficient than doing a query of queries.
<cfoutput query="resources" group="type">
<cfset noofrecords= 0>
<cfoutput>
<cfset noofrecords++>
</cfoutput>
<h4>#type# (#noofrecords# of #resources.recordcount#)</h4>
<cfoutput>
#name#
</cfoutput>
</cfoutput>
I am afraid that you would have to do some counting yourself. There is no RecordCount for the
nested grouped output, because it is really all the same query, CF is just
doing a little formatting for you.
You could get the count in the original query if you wanted an alternative method, but, you will need to test the performance before you go down this route (although using Query of Queries can also have performance issues as there are no indexes).
SELECT f1.id, f1.name, f1.type, f2.typeCount
FROM foo f1
INNER JOIN (
SELECT COUNT(type) as typeCount, type
FROM foo
GROUP BY type
) f2 on f1.type = f2.type
ORDER BY f1.type, f1.name
Then in the CF you can do:
<cfoutput query="resources" group="type">
<h4>#resources.type# (#resources.typeCount# of #resources.recordCount#)</h4>
<cfoutput>
#resources.name#
</cfoutput>
</cfoutput>
As a side note, in CF10 you can also group inside a cfloop with query like so:
<cfloop query="resources" group="type">
<h4>#resources.type# (#resources.typeCount# of #resources.recordCount#)</h4>
<cfloop>
#resources.name#
</cfloop>
</cfloop>
Another solution is:
<!--- I chose the pipe delimiter | names of things often contain commas,
otherwise, use any delimiter you like --->
<cfset TypesList = Valuelist(resources.type,"|")>
<cfoutput query="resources">
<h4>#Type# (#ListValueCount(TypesList,Type,"|")# of #resources.recordcount#)</h4>
<cfoutput>#name#</cfoutput>
</cfoutput>
While this will also work for other applications, it won't work for everything. It expects the Category count ('Type' here) to be unique (no two types with the same name). A better way to handle this is to count based on an ID rather than a name. You might have, for instance, a category setup like this
Fruits
Red Ones
Strawberries
Raspberries
Yellow Ones
Bananas
Vegetables
Green ones
Green peppers
Red Ones
Red Peppers
Tomatoes
Based on Type (as a string), the output would say Red Ones (4), Yellow Ones (1), Green Ones (1), Red Ones (4), but in a category-products relational table structure, coutning based on unique ID of the category would retrieve accurate counts.
To get the count of the nested data and display it where you say you want it, I would do this:
<cfoutput query = "rescources" group = "type">
query of queries to get the count this type
output the type and the count
<cfoutput>
output nested data
</cfoutput>
</cfoutput>