Coldfusion Looping over query - coldfusion

I have a query result something like
ID IDParent Name Title
--------------------------------------
1 -1 Test1 Test1_Title
2 -1 Test2 Test2_Title
3 -1 Test3 Test3_Title
4 2 SubTest2 SubTest2_Title
5 2 SubTest3 SubTest3_Title
6 2 SubTest4 SubTest4_Title
7 3 SubTest6 SubTest8_Title
8 3 SubTest8 SubTest10_Title
with menu and submenu options.I want to loop over the menu option where IDParent is -1 and after each menu Item where IDParent -1 I would like to loop its child items.
Does coldfusion provides such grouping when looping over queries?
Thanks

CFOUTPUT supports query groupings as well.
<cfquery name="qGetTests" datasource="#DSN#">
SELECT ID, IDParent, Name, Title
FROM Menu
ORDER BY IDParent, Name
</cfquery>
<cfoutput query="qGetTests" group="IDParent">
#IDParent#<br />
<cfoutput>
#ID# #Name# #Title#<br />
</cfoutput>
</cfoutput>

That's pretty easy with Query of Queries (QoQ) and a little recursion:
<!-- database query, sorted in the way you want to display the results -->
<cfquery name="Menu" datasource="#YourDSN#">
SELECT ID, IDParent, Name, Title
FROM Menu
ORDER BY Name
</cfquery>
<!-- output menu -->
<cfset OutputQueryRecursive(Menu, -1)>
<!-- support function -->
<cffunction name="OutputQueryRecursive">
<cfargument name="BaseQuery" type="query" required="yes">
<cfargument name="ParentId" type="numeric" required="yes">
<cfquery name="CurrLevel" dbtype="query">
SELECT * FROM BaseQuery WHERE IDParent = #ParentId#
</cfquery>
<cfif CurrLevel.RecordCount gt 0>
<ul>
<cfoutput query="CurrLevel">
<li id="menu_#ID#">
<span title="#HTMLEditFormat(Title)#">#HTMLEditFormat(Name)#</span>
<cfset OutputQueryRecursive(BaseQuery, ID)>
</li>
</cfouptut>
</ul>
</cfif>
</cffunction>

If you have any control of the SQL generating that query result, you could consider getting the DB to get you the data back in the right format in the first place. Approaches for Oracle and SQL server are covered here and there's some options for mySQL here
If your menu data is always going to be small, then there'll be no problem with Tomalak's solution, but if you're ever going to have large numbers of menu items then I'd test that it still performs ok.

consider qTestQuery contains the values
<cfset qTestQuery1 = qTestQuery>
<cfloop query="qTestQuery">
<cfif qTestQuery.IDParent eq -1>
<span class="main-menu">#qTestQuery.name#</span>
</cfif>
<cfset local.parentId = qTestQuery.IDParent>
<cfloop query="qTestQuery1">
<cfif qTestQuery1.IDParent eq local.parentId>
<span class="sub-menu">#qTestQuery1.name#</span>
</cfif>
</cfloop>
</cfloop>

Related

looping over a list with multiple and selected the selected ones

I'm working on code where I am getting two lists. I'm trying to use those lists to pre-select items in select box having the "multiple" attribute. However, I am unable to maintain the selections.
Code:
<select name="sbbox" id="sbbox" class="can" multiple>
<cfloop list="#lstFinds#" index="k" delimiters=",">
<option value="#k#" <cfif #listfindnocase(k,getproducts.ptype)#>selected</cfif>>#k#</option>
</cfloop>
</select>
Sample values:
lstFinds = abc,xyz
getproducts.ptype also contains values like abc,xyz
I want to keep both selected, if both values exists for the user. If one exists, keep one selected. If none are selected, keep none.
I also tried using listContains, but it did not work.
Transferred from pastebin linkL
The ptype values are coming as comma separated in the database ie "abc,wed,mon,def". Whatever those values are, I need to match and selected the ones which have the same value in the listFind. I hope I made it clearer.
<cfset lstFinds = 'abc,xyz,def,www,kkr,mon,tue,wed'>
<cfquery name="getproducts" datasource="cdfg">
select ptype
from
mytable
where
ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#id#">
</cfquery>
<select name="sbbox" id="sbbox" class="can" multiple>
<cfloop list="#lstFinds#" index="k" delimiters=",">
<option value="#k#" <cfif #listfindnocase(k,getproducts.ptype)#>selected</cfif>>#k#</option>
</cfloop>
</select>
There are three things wrong with this:
<cfloop list="#lstFinds#" index="k" delimiters=",">
<option value="#k#" <cfif #listfindnocase(k,getproducts.ptype)#>selected</cfif>>#k#</option>
</cfloop>
First, as others have mentioned, in the listfindnocase function, the list comes first.
Next, the reason you are not getting the desired results after sorting out the first problem, is that getproducts.ptype is not a list. It is the value from the first row of the query. To get all the values in a list, use the valuelist() function.
Finally, the correct syntax for having an option selected is selected="selected". So the code block above should be this:
<cfloop list="#lstFinds#" index="k" delimiters=",">
<option value="#k#"
<cfif listfindnocase(ValueList(getproducts.ptype), k)>
selected="selected"
</cfif>>#k#
</option>
</cfloop>
Ok, the asker provided the following code via a Pastebin
<cfset lstFinds = 'abc,xyz,def,www,kkr,mon,tue,wed'>
<cfquery name="getproducts" datasource="cdfg">
select ptype
from
mytable
where
ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#id#">
</cfquery>
these are coming from database column ptype of a table as: abc,wed,mon,def
<select name="sbbox" id="sbbox" class="can" multiple>
<cfloop list="#lstFinds#" index="k" delimiters=",">
<option value="#k#" <cfif #listfindnocase(k,getproducts.ptype)#>selected</cfif>>#k#</option>
</cfloop>
</select>
I ran the following code as at cflive.net
<cfset lstFinds = 'abc,xyz,def,www,kkr,mon,tue,wed'>
<cfset getProducts = {pType = "abc,wed,mon,def"}>
<cfoutput><select name="sbbox" id="sbbox" class="can" multiple>
<cfloop list="#lstFinds#" index="k" delimiters=",">
<option value="#k#" <cfif #listfindnocase(getproducts.ptype,k)#>selected</cfif>>#k#</option>
</cfloop>
</select></cfoutput>
When I run this code, abc, def, mon, and wed are selected. Should these not be selected, or should others be checked?
This code appears to be doing what you are asking. The only changes I made
Changed the logic as per what Matt suggested in question comments.
Because I don't have the data to run a query against, I setup a getProducts structure with the key "pType" and the values as Asker suggested they might be. For the purpose here, my sample struct is functionally identical to a one-row query.
Finally, is it possible that you're trying to pull this from multiple rows? You might try
<cfset variables.ptypes = valuelist(getproducts.ptype)>
<cfoutput><select name="sbbox" id="sbbox" class="can" multiple>
<cfloop list="#lstFinds#" index="k" delimiters=",">
<option value="#k#" <cfif #listfindnocase(variables.ptypes,k)#>selected</cfif>>#k#</option>
</cfloop>
</select></cfoutput>

ColdFusion cfoutput & excluding items

<cfif dir.name IS NOT "Thumbs.db">
This code excludes Thumbs.db from being called in the cfoutput query, but what if I want another file excluded? Not sure how to exclude more than one item though.
Right now have
<cfset counter = 1 />
<cfoutput query="dir1">
<cfif !listfindNoCase( 'Thumbs.db,2. Electric Accounts Tracking Report.xls,1. Electric Accounts Performance Analytics.xls', dir1.name) >
<a href="/091_AU20100226/020_Cost_Analyses/010_Electric/Flatten_Files/#dir1.name#" target="_blank">
#dir1.name#</a><br />
<cfset counter++ /> </cfif> </cfoutput>
You can use listFind() or listFindNoCase().
<cfif !listfindNoCase( 'Thumbs.db,otherFile.txt', dir.name) >
...do stuff...
</cfif>

Finding images not referenced in a DB table

I get a query of all the files (images) in a directory
<cfset local.teamBase = "#GetDirectoryFromPath(GetBaseTemplatePath())#teamlogo\">
<cfdirectory action="LIST" directory="#local.teamBase#" name="rc.qryTeamLogo">
I later loop over all these filenames, skipping those that are in use
<select name="Logo" <cfif deleted>disabled="disabled"</cfif>>
<option></option>
<cfloop query="rc.qryTeamLogo">
<cfif name EQ mylogo>
<option value="#name#" selected>#name#</option>
<cfelseif request.objTeam.isUsed(name) EQ 1 OR name EQ "thumbs.db">
<!--- Do not show --->
<cfelse>
<option value="#name#">#name#</option>
</cfif>
</cfloop>
</select>
The isUsed() function looks like
<cffunction name="isUsed" returntype="boolean" output="false">
<cfargument name="logo" required="true" type="string">
<cfquery name="qryUsed" datasource="#application.DSN#">
SELECT logo
FROM CCF.team
WHERE logo = <cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#arguments.logo#">
AND Deleted = 0
</cfquery>
<cfif qryUsed.recordcount EQ 0>
<cfreturn false>
</cfif>
<cfreturn true>
</cffunction>
The problem with this is as more and more files get added, this gets slower and slower.
What I would really like a single query that can assemble the whole thing
You can run one query before your select box
<cfquery name="qryLogos" datasource="#application.DSN#">
SELECT logo
FROM CCF.team
WHERE logo IN (<cfqueryparam cfsqltype="CF_SQL_VARCHAR" value="#valueList(rc.qryTeamLogo.name)#" list="true">)
AND Deleted = 0
</cfquery>
Then set all those logo's into an array and add thumbs.db too, so you have one less check to do.
<cfset allLogs = listToArray(valueList(qryLogos.logo))>
<cfset arrayAppend(allLogs,'thumbs.db')>
and then change your elseif to this
<cfelseif arrayFind(allLogs,name)>
<!--- don't display --->
Load the file information into XML. Shread the XML into a table and JOIN on that.
<cfset local.teamBase = "#GetDirectoryFromPath(GetBaseTemplatePath())#teamlogo\">
<cfset local.qryTeamLogo = DirectoryList(local.teamBase, false, "query")>
<cfsavecontent variable="local.xmlTeamLogo">
<ul class="xoxo">
<cfoutput query="local.qryTeamLogo">
<li><code>#xmlformat(name)#</code></li>
</cfoutput>
</ul>
</cfsavecontent>
<cfquery name="local.qryResult">
DECLARE #xmlTeamLogo xml = <cfqueryparam cfsqltype="CF_SQL_varchar" value="#local.xmlTeamLogo#">
SELECT Value AS Name
FROM dbo.udfXOXORead(#xmlTeamLogo)
WHERE NOT Value IN (
SELECT Logo
FROM CCF.Team WITH (NOLOCK)
WHERE Deleted = 0
)
</cfquery>
The Table Valued Function
create function [dbo].[udfXOXORead](#xmlData xml)
returns #tblmessage TABLE (
[value] nvarchar(max) NULL
)
as
begin
INSERT INTO #tblMessage
SELECT Tbl.Col.value('(code)[1]', 'nvarchar(max)') AS [value]
FROM #xmlData.nodes('/ul/li') Tbl(Col)
RETURN
end

Need advice.. dynamic td tr

Hi am doing dynamic TR and TD in coldfusion, but apparently i am trying this kind of screen. here is my try with colfusion
<cfif variables.newrow EQ true>
<tr align="center">
</cfif>
<cfoutput query="gResults">
<cfquery datasource="#request.dsn#" name="nextqueries">
Query
</cfquery>
<td height="30" valign="middle"><strong>
<a href="viewdir.cfm?catID=#val(pic_id)#
<cfif isDefined('url.l') and url.l neq ''>&l=#url.l#</cfif>">
#pic_cat_name#</a></strong><br>
<cfloop query="nextqueries">
<cfquery datasource="#request.dsn#" name="showanother">
select * from
mytable as grlist
where pic_cid =
<cfqueryparam cfsqltype="cf_sql_numeric" value="#Trim(nextqueries.pic_id)">
</cfquery>
»
<a href="viewdir.cfm?catID=#val(pic_id)#
<cfif isDefined('url.l') and url.l neq ''>&l=#url.l#</cfif>">#pic_cat_name#
</a> </cfloop></td>
<cfif gResults.currentRow MOD 4 EQ 0>
</tr>
<cfset variables.newrow = true>
<cfelse>
<cfset variables.newrow = false>
</cfif>
</cfoutput>
trying to do like this:
http://screencast.com/t/oso4jkhBm3
There are a lot of potential improvements, but this answer will only deal with table rows. You start with:
<cfif variables.newrow EQ true>
<tr align="center">
</cfif>
Now I'm going to look for the closing tag. The only one I see is here:
<cfif gResults.currentRow MOD 4 EQ 0></tr></cfif>
and that line is inside a query loop. What this means is that you might have more than one closing tag, or you might not have any. You need exactly one. To solve this specific problem, you have to do this:
<cfif variables.newrow EQ true>
<tr align="center">
code to populate this table row
</cfif>
When you get that part sorted out, we can look at what goes into the details.
To get data to display in an html table from top to bottom then left to right like in the image linked in your question, you can do something similar to the following.
<!--- Get query results --->
<cfset arrayOFValues = arraynew(1)>
<cfset queryResults = querynew("Col1,Col2,Col3")>
<!--- Fill query with example data --->
<cfloop from="1" to="25" index="i">
<cfset queryaddrow(queryResults)>
<cfset querySetCell(queryResults, "Col1", "Col1 Row " & i)>
<cfset querySetCell(queryResults, "Col2", "Col1 Row " & i)>
<cfset querySetCell(queryResults, "Col3", "Col1 Row " & i)>
</cfloop>
<!--- Now have a query named queryResults with 25 rows --->
<!--- Set the number of columns and calculate the number of rows needed --->
<cfset numberOfColumns = 3>
<cfset rowsNeeded = ceiling(queryResults.recordcount / numberOfColumns)>
<cfoutput>
<table>
<cfloop from="1" to="#rowsNeeded#" index="curRow">
<tr>
<cfloop from="0" to="#numberOfColumns-1#" index="curCol">
<td>
#queryResults.Col1[(rowsNeeded * curCol) + curRow]#
</td>
</cfloop>
</tr>
</cfloop>
</table>
</cfoutput>
The first part is just creating a query result. I then find the number of rows that is needed to display the records by dividing the number of results returned in the query by the number of columns to be displayed. The ceiling is required for when the result is not a whole number.
We have to loop each column record with in the row to get the desired index. To find the required index of the field we have to take the row that’s being displayed + the column were on times the the number of rows that will be displayed.
If you know the number of columns you can hard code for them in the following manner and eliminate the inner loop.
<tr>
<td>#queryResults.Col1[3 * rowsNeeded]#</td>
<td>#queryResults.Col1[3 + 1*rowsNeeded]#</td>
<td>#queryResults.Col1[3 + 2*rowsNeeded]#</td>
</tr>

Nesting queries in CF

I'm using this code to display a list of platforms. If a platformID was specified upon entering the page, I would like to create a list of genres underneath the specified platform.
browse.cfm was accessed via a link that specified a platformID of 1
browse.cfm will list all available platforms
browse.cfm will now list all available genres under platformID of 1.
<ul>
<li>Browse</li>
<cfoutput query="qGetPlatforms">
<li>
#qGetPlatforms.pName#
<cfif URL.platformID EQ qGetPlatforms.platformID>
<ul>
<cfoutput query="qGetGenres">
<li>#qGetGenres.gName#</li>
</cfoutput>
</ul>
</cfif>
</li>
</cfoutput>
</ul>
By using this approach, however, I'm getting an invalid nesting configuration. How do I fix this? Or is there another approach to achieve the same idea?
Thanks
MY queries:
<!---Get platforms--->
<cffunction
name="fGetPlatforms"
access="public"
returntype="query"
output="false"
hint="I get all the platforms">
<!---Local var--->
<cfset qGetPlatforms = "">
<!---Database query--->
<cfquery name="qGetPlatforms" datasource="#REQUEST.datasource#">
SELECT
platforms.platformID,
platforms.platformName AS pName
FROM
platforms
</cfquery>
<cfreturn qGetPlatforms>
</cffunction>
<!---Get genres--->
<cffunction
name="fGetGenres"
access="public"
returntype="query"
output="false"
hint="I get all the genres">
<!---Local var--->
<cfset qGetGenres = "">
<!---Database query--->
<cfquery name="qGetGenres" datasource="#REQUEST.datasource#">
SELECT
genres.genreID,
genres.genreName AS gName
FROM
genres
</cfquery>
<cfreturn qGetGenres>
</cffunction>
You can use <cfloop query="qGetGenres"></cfloop>, they can be nested.
IMO, using cfoutput for looping over the queries is old style and should be avoided. Use cfoutput for output, cfloop for looping and you'll have more readable code.
more food for thought is to use an inner join between the two tables, combine and retrieve everything in one query and then use cfoutput's group attribute to display the results:
<cfset URL.platformID = int(val(URL.platformID))>
<cfquery name="getPlatformsAndGenres" datasource="#REQUEST.datasource#">
SELECT
p.platformID AS platformID
,p.platformName AS pName
,g.genreID AS genreID
,g.genreName AS gName
FROM
platforms p
INNER JOIN genres g
ON p.platformID = g.platformID
WHERE
p.platformID = <cfqueryparam cfsqltype="cf_sql_integer" value="#URL.platformID#">
ORDER BY
pName
,genreName
</cfquery>
Once you have everything in one query, you can use <cfoutput query="getPlatformsAndGenres" group="pName">
to lessen your code:
<ul>
<li>Browse</li>
<cfoutput query="getPlatformsAndGenres" group="pName">
<li>
#pName#
<ul>
<cfoutput>
<li>#gName#</li>
</cfoutput>
</ul>
</cfif>
</li>
</cfoutput>
</ul>