Output HTML from XML in ColdFusion - html

I have a query running to gather an adjacency list and then generate an XML object of that list as a tree. Next I need to output that tree as a simple HTML.
I would like my output of XML document to be:
<ul>
<li margin="5">Title
<ul>
<li margin="10">Title</li>
</ul>
</li>
</ul>
Here is what I have coded so far:
<cfquery name="nodeTable" datasource="#database.ds#">
SELECT [mc_location].[id],[mc_location].[title], [mc_location].[parent_id] FROM [mc_location]
LEFT JOIN [mc_location_type] ON [mc_location].[id] = [mc_location_type].[location_id]
WHERE [mc_location_type].[category] = 'staff'
</cfquery>
<cffunction name="outputChildNodes" access="public" returntype="void" output="true">
<cfargument name="nodeTable" type="query" required="true" hint="I am the node query object."/>
<cfargument name="parent_id" type="numeric" required="false" default="0"/>
<cfset var local = {}/>
<cfquery name="local.childNodes" dbtype="query">
SELECT id, parent_id, title
FROM arguments.nodeTable
WHERE parent_id = <cfqueryparam value="#arguments.parent_id#" cfsqltype="cf_sql_integer" />
ORDER BY id ASC
</cfquery>
<cfloop query="local.childNodes">
<child id="#local.childNodes.id#" parent-id="#local.childNodes.parent_id#" name="#local.childNodes.title#">
<cfset outputChildNodes(arguments.nodeTable, local.childNodes.id)/>
</child>
</cfloop>
<cfreturn/>
</cffunction>
<!--- Build the node XML document recursively. --->
<cfxml variable="nodeTree">
<childern>
<!--- Output the root-level nodes. --->
<cfset outputChildNodes( nodeTable ) />
</childern>
</cfxml>
<!--- Render the XML document. --->
<cfloop index="childern" array="#nodeTree.childern#">
<cfloop index="child" array="#childern#">
<cfif isStruct(child.XmlAttributes)>
<cfdump var="#child[1].XmlAttributes#"/>
</cfif>
</cfloop>
</cfloop>
Thank you!

The problem I was experiencing had to do with not properly defining variable scopes (e.g. using "var" in cfset, using arguments.parameter, etc.), which was necessary in order to recursively call a function. Presented below is a brief solution.
<!--- query for parent child tree --->
<cfquery name="tree_nodes" datasource="#sonis.ds#">
SELECT [location].[id],[location].[title], [location].[parent_id] FROM [location]
LEFT JOIN [location_category] ON [location].[id] = [location_category].[location_id]
WHERE [location_category].[category] = 'staff'
</cfquery>
<!--- build tree from adjacency list function --->
<cffunction name="build_tree" access="public" output="true">
<cfargument var name="tree_nodes" type="query" required="true"/>
<cfargument var name="parent_id" type="numeric" required="false" default="0"/>
<cfargument var name="depth" type="numeric" required="false" default="0"/>
<cfset var local = {}/>
<cfquery name="local.child_node" dbtype="query">
SELECT id, parent_id, title
FROM arguments.tree_nodes
WHERE parent_id = <cfqueryparam value="#arguments.parent_id#" cfsqltype="cf_sql_integer" />
ORDER BY id ASC
</cfquery>
<cfset var branch = {}/>
<cfset var counter = 1/>
<cfloop query="local.child_node">
<cfset local.depth = arguments.depth/>
<cfset branch[counter++] = {
'id' = '#local.child_node.id#',
'title' = '#local.child_node.title#',
'parent_id' = '#local.child_node.parent_id#',
'depth' = local.depth,
'children' = build_tree(arguments.tree_nodes, local.child_node.id,++local.depth)
} />
</cfloop>
<cfreturn branch/>
</cffunction>
<!--- print tree as select box function --->
<cffunction name="print_tree_select" access="public" output="true">
<cfargument var name="tree" type="struct" required="true"/>
<cfargument var name="selected" type="numeric" required="false" default="0"/>
<cfargument var name="child" type="numeric" required="false" default="0"/>
<cfif child eq '0'><select name="select_tree"><option value="null"></option></cfif>
<cfloop from="1" to="#StructCount(arguments.tree)#" index="a">
<option value="#arguments.tree[a]['id']#"<cfif #arguments.selected# eq #arguments.tree[a]['id']#> selected</cfif>>
<cfif #arguments.tree[a]['depth']# GT 0>
#RepeatString('--', arguments.tree[a]['depth'])#
</cfif>
#arguments.tree[a]['title']#
</option>
<cfif StructKeyExists(arguments.tree[a], 'children') AND StructCount(arguments.tree[a]['children']) GT 0>
#print_tree_select(arguments.tree[a]['children'],arguments.selected, 1)#
</cfif>
</cfloop>
<cfif child eq '0'></select></cfif>
</cffunction>
<!--- print tree as list function --->
<cffunction name="print_tree_list" access="public" output="true">
<cfargument var name="tree" type="struct" required="true"/>
<ul style="list-style-type: circle;">
<cfloop from="1" to="#StructCount(arguments.tree)#" index="local.i">
<li>
<cfform method="post" name="edit">
#arguments.tree[local.i]['title']#
<cfinput type="hidden" name="id" value="#arguments.tree[local.i]['id']#"/>
<cfinput type="Submit" name="command" value="Edit"/>
</cfform>
<cfif StructKeyExists(arguments.tree[local.i], 'children') AND StructCount(arguments.tree[local.i]['children']) GT 0>
#print_tree_list(arguments.tree[local.i]['children'])#
</cfif>
</li>
</cfloop>
</ul>
</cffunction>

Related

cffunction: second argument based on first argument

I have following code that first creates an object and afterwards it calls a function called init.
<cfset SESSION.objWatchlist = createObject("component", "path") />
<cfset SESSION.objWatchlist.init(session.addressid) />
So far so good. The init function looks like this:
<cffunction name="init" access="remote" output="false">
<cfargument name="addressID" type="numeric" required="true" />
<cfset THIS.ADDRESSID = arguments.addressID />
<cfset THIS.WATCHLIST = arrayNew(1) />
<cfset initWatchlistArray() />
<cfreturn this />
</cffunction>
Now I want to add another argument to the cffunction. I will call it like this:
<cfset SESSION.objWatchlist.init("addressid", session.addressid) />
and
<cfset SESSION.objWatchlist.init("sessionid", session.sessionid) />
I add some new lines in the cffunction:
<cffunction name="init" access="remote" output="false">
<cfargument name="typeOfID" type="string" required="true" />
<cfif arguments.typeOfID eq "addressid">
<cfargument name="addressID" type="numeric" required="true" />
<cfset THIS.ADDRESSID = arguments.addressID />
<cfelseif arguments.typeOfID eq "sessionid">
<cfargument name="sessionID" type="string" required="true" />
<cfset THIS.SESSIONID = arguments.sessionID />
</cfif>
<cfset THIS.WATCHLIST = arrayNew(1) />
<cfset initWatchlistArray() />
<cfreturn this />
</cffunction>
But everytime I get the error "The tag must be nested inside a CFFUNCTION tag".
Now my question is how can I write a function in which the second argument is based on the first one?
Is this even possible?
Thank you.
At the end I have found a solution that looks like that:
First I call the function:
<cfset SESSION.objWatchlist.init(addressID=session.addressid) />
<cfset SESSION.objWatchlist.init(sessionID=session.sessionid) />
I changed the function to following:
<cffunction name="init" access="remote" output="false">
<cfargument name="addressID" type="numeric" required="false" />
<cfargument name="sessionID" type="string" required="false" />
<cfif isDefined("arguments.addressid")>
<cfset THIS.ADDRESSID = arguments.addressID />
<cfelseif isDefined("arguments.sessionid")>
<cfset THIS.SESSIONID = arguments.sessionid />
</cfif>
<cfset THIS.WATCHLIST = arrayNew(1) />
<cfset initWatchlistArray() />
<cfreturn this />
</cffunction>
When calling the function I can call them with the name of the argument in the cffunction.
Afterwards I can check which one is defined.
This is too long for a comment, but I have a little cleanup on this
SESSION.objWatchlist.init(addressID=session.addressid);
SESSION.objWatchlist.init(sessionID=session.sessionid);
any function init(numeric addressID, string sessionid) access="remote" output="false" {
if (arguments.keyexists(("addressid")) { THIS.ADDRESSID = arguments.addressID; }
else if (arguments.keyexists("sessionid") { THIS.SESSIONID = arguments.sessionid; }
THIS.WATCHLIST = [];
initWatchlistArray();
return this;
}
Note: the use of .keyexists(). This is a faster check because it does not have to look through every scope that could have something that matches.
Also note it is easier to blank out an array.

How to parse JSON response and insert into Query

I have the following JSON response and am really lost on how to insert that into a ColdFusion query using a loop.
The response is like this:
{
"status":"OK",
"data":{
"group_id":1522413460,
"0":{
"id":"1522413460-1",
"customid":"",
"customid1":"",
"customid2":"",
"mobile":"0000000000",
"status":"AWAITED-DLR"
},
"1":{
"id":"1522413460-2",
"customid":null,
"customid1":null,
"customid2":null,
"mobile":"0000000000",
"status":"AWAITED-DLR"
},
"2":{
"id":"1522413460-3",
"customid":null,
"customid1":null,
"customid2":null,
"mobile":"0000000000",
"status":"AWAITED-DLR"
},
"3":{
"id":"1522413460-4",
"customid":null,
"customid1":null,
"customid2":null,
"mobile":"",
"status":"INV-NUMBER"
}
},
"message":"Campaign of 4 numbers Submitted successfully."
}
I am using a jsondecode function from cflib to decode the response of JSON into a nested structure:
<cffunction name="jsonencode" access="remote" returntype="string" output="No" hint="Converts data from CF to JSON format">
<cfargument name="data" type="any" required="Yes" />
<cfargument name="queryFormat" type="string" required="No" default="query" /> <!-- query or array -->
<cfargument name="queryKeyCase" type="string" required="No" default="lower" /> <!-- lower or upper -->
<cfargument name="stringNumbers" type="boolean" required="No" default=false >
<cfargument name="formatDates" type="boolean" required="No" default=false >
<cfargument name="columnListFormat" type="string" required="No" default="string" > <!-- string or array -->
<cfset var jsonString = "" />
<cfset var tempVal = "" />
<cfset var arKeys = "" />
<cfset var colPos = 1 />
<cfset var i = 1 />
<cfset var column = ""/>
<cfset var datakey = ""/>
<cfset var recordcountkey = ""/>
<cfset var columnlist = ""/>
<cfset var columnlistkey = ""/>
<cfset var dJSONString = "" />
<cfset var escapeToVals = "\\,\"",\/,\b,\t,\n,\f,\r" />
<cfset var escapeVals = "\,"",/,#Chr(8)#,#Chr(9)#,#Chr(10)#,#Chr(12)#,#Chr(13)#" />
<cfset var _data = arguments.data />
<!--- BOOLEAN --->
<cfif IsBoolean(_data) AND NOT IsNumeric(_data) AND NOT ListFindNoCase("Yes,No", _data)>
<cfreturn LCase(ToString(_data)) />
<!--- NUMBER --->
<cfelseif NOT stringNumbers AND IsNumeric(_data) AND NOT REFind("^0+[^\.]",_data)>
<cfreturn ToString(_data) />
<!--- DATE --->
<cfelseif IsDate(_data) AND arguments.formatDates>
<cfreturn '"#DateFormat(_data, "medium")# #TimeFormat(_data, "medium")#"' />
<!--- STRING --->
<cfelseif IsSimpleValue(_data)>
<cfreturn '"' & ReplaceList(_data, escapeVals, escapeToVals) & '"' />
<!--- ARRAY --->
<cfelseif IsArray(_data)>
<cfset dJSONString = createObject('java','java.lang.StringBuffer').init("") />
<cfloop from="1" to="#ArrayLen(_data)#" index="i">
<cfset tempVal = jsonencode( _data[i], arguments.queryFormat, arguments.queryKeyCase, arguments.stringNumbers, arguments.formatDates, arguments.columnListFormat ) />
<cfif dJSONString.toString() EQ "">
<cfset dJSONString.append(tempVal) />
<cfelse>
<cfset dJSONString.append("," & tempVal) />
</cfif>
</cfloop>
<cfreturn "[" & dJSONString.toString() & "]" />
<!--- STRUCT --->
<cfelseif IsStruct(_data)>
<cfset dJSONString = createObject('java','java.lang.StringBuffer').init("") />
<cfset arKeys = StructKeyArray(_data) />
<cfloop from="1" to="#ArrayLen(arKeys)#" index="i">
<cfset tempVal = jsonencode( _data[ arKeys[i] ], arguments.queryFormat, arguments.queryKeyCase, arguments.stringNumbers, arguments.formatDates, arguments.columnListFormat ) />
<cfif dJSONString.toString() EQ "">
<cfset dJSONString.append('"' & arKeys[i] & '":' & tempVal) />
<cfelse>
<cfset dJSONString.append("," & '"' & arKeys[i] & '":' & tempVal) />
</cfif>
</cfloop>
<cfreturn "{" & dJSONString.toString() & "}" />
<!--- QUERY --->
<cfelseif IsQuery(_data)>
<cfset dJSONString = createObject('java','java.lang.StringBuffer').init("") />
<!--- Add query meta data --->
<cfif arguments.queryKeyCase EQ "lower">
<cfset recordcountKey = "recordcount" />
<cfset columnlistKey = "columnlist" />
<cfset columnlist = LCase(_data.columnlist) />
<cfset dataKey = "data" />
<cfelse>
<cfset recordcountKey = "RECORDCOUNT" />
<cfset columnlistKey = "COLUMNLIST" />
<cfset columnlist = _data.columnlist />
<cfset dataKey = "data" />
</cfif>
<cfset dJSONString.append('"#recordcountKey#":' & _data.recordcount) />
<cfif arguments.columnListFormat EQ "array">
<cfset columnlist = "[" & ListQualify(columnlist, '"') & "]" />
<cfset dJSONString.append(',"#columnlistKey#":' & columnlist) />
<cfelse>
<cfset dJSONString.append(',"#columnlistKey#":"' & columnlist & '"') />
</cfif>
<cfset dJSONString.append(',"#dataKey#":') />
<!--- Make query a structure of arrays --->
<cfif arguments.queryFormat EQ "query">
<cfset dJSONString.append("{") />
<cfset colPos = 1 />
<cfloop list="#_data.columnlist#" delimiters="," index="column">
<cfif colPos GT 1>
<cfset dJSONString.append(",") />
</cfif>
<cfif arguments.queryKeyCase EQ "lower">
<cfset column = LCase(column) />
</cfif>
<cfset dJSONString.append('"' & column & '":[') />
<cfloop from="1" to="#_data.recordcount#" index="i">
<!--- Get cell value; recurse to get proper format depending on string/number/boolean data type --->
<cfset tempVal = jsonencode( _data[column][i], arguments.queryFormat, arguments.queryKeyCase, arguments.stringNumbers, arguments.formatDates, arguments.columnListFormat ) />
<cfif i GT 1>
<cfset dJSONString.append(",") />
</cfif>
<cfset dJSONString.append(tempVal) />
</cfloop>
<cfset dJSONString.append("]") />
<cfset colPos = colPos + 1 />
</cfloop>
<cfset dJSONString.append("}") />
<!--- Make query an array of structures --->
<cfelse>
<cfset dJSONString.append("[") />
<cfloop query="_data">
<cfif CurrentRow GT 1>
<cfset dJSONString.append(",") />
</cfif>
<cfset dJSONString.append("{") />
<cfset colPos = 1 />
<cfloop list="#columnlist#" delimiters="," index="column">
<cfset tempVal = jsonencode( _data[column][CurrentRow], arguments.queryFormat, arguments.queryKeyCase, arguments.stringNumbers, arguments.formatDates, arguments.columnListFormat ) />
<cfif colPos GT 1>
<cfset dJSONString.append(",") />
</cfif>
<cfif arguments.queryKeyCase EQ "lower">
<cfset column = LCase(column) />
</cfif>
<cfset dJSONString.append('"' & column & '":' & tempVal) />
<cfset colPos = colPos + 1 />
</cfloop>
<cfset dJSONString.append("}") />
</cfloop>
<cfset dJSONString.append("]") />
</cfif>
<!--- Wrap all query data into an object --->
<cfreturn "{" & dJSONString.toString() & "}" />
<!--- UNKNOWN OBJECT TYPE --->
<cfelse>
<cfreturn '"' & "unknown-obj" & '"' />
</cfif>
</cffunction>
Update #1: with DeserializeJSON, I get the following screen shot. If I loop over results called data, how to nest the inside ones?
JSON / CFML - Looping over an array of structs
I asked something similar recently.
Looks like you have an array of structs. You can deserialize the JSON as others have described and loop through it as shown in my link.
Edit just for clarity's sake
Here's the code that worked for me. I've edited out one step since the response you're getting doesn't seem to contain encoded characters.
<cfset requestBody = #(toString(getHttpRequestData().content)/>
<cfset ArrayOfStructs = deserializeJson(requestBody)>
<cfloop array="#ArrayOfStructs#" index="i">
<cfquery name="doodoodoo" datasource="CRM">
INSERT INTO TimeAppTest
(
EmployeeID,
lat,
long,
TimoStampo
)
VALUES
(
'#i.barcode#',
'#i.lat#',
'#i.long#',
'#i.time#'
)
</cfquery>
</cfloop>
The JSON was something like so, explaining the different variable names.
{"barcode":"CSS1035","scannerID":"3e81b04aa521a05e","time":"2015-08-11 08:30:27.232","lat":32.4001579,"long":-110.0403455},
{"barcode":"CSS1959","scannerID":"3e81b04aa521a05e","time":"2015-08-11 08:30:29.366","lat":32.4001579,"long":-110.0403455},
{"barcode":"CSS1649","scannerID":"3e81b04aa521a05e","time":"2015-08-11 08:30:31.642","lat":32.4001579,"long":-110.0403455}
use DeserializeJSON() to convert to a struct result.
loop through the result.data with <cfloop>
use <cfquery> to construct a SQL INSERT statement, pass values by <cfqueryparam>
ColdFusion doc:
https://wikidocs.adobe.com/wiki/display/coldfusionen/Home

Triple Chained/Related Selects ColdFusion 9

Using a AJAX Based Triple Chained/Related Selects in Cold Fusion
I am getting Error Invoking CFC : Internal Server Error Adding cfdebug tag doesn't help
The Triple Chained/Related Select works fine, but generates the error. How can I make stop it from happening. I believe my CFC is coded correctly
Any help will be appreciated
Already Tried bindonload=false for Second Select Does not help Also Tried bindonload=false for First Select Options dont load
Here is my code
CFC:
<!--- Get array of media types --->
<cffunction name="getDevice" access="remote" returnType="array">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(2)>
<cfset var i=0>
<!--- Get data --->
<cfquery name="data" datasource="#dsn#">
SELECT device_id, device_list
FROM pa_device
ORDER BY device_list
</cfquery>
<!--- Convert results to array --->
<cfloop index="i" from="1" to="#data.RecordCount#">
<cfset result[i][1]=data.device_id[i]>
<cfset result[i][2]=data.device_list[i]>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
<!--- Get art by media type --->
<cffunction name="getProblem" access="remote" returnType="array">
<cfargument name="device_id" type="numeric" required="true">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(2)>
<cfset var i=0>
<!--- Get data --->
<cfquery name="data" datasource="#dsn#">
SELECT problem_id, problem_list
FROM pa_problem
WHERE device_id = #ARGUMENTS.device_id#
ORDER BY problem_list
</cfquery>
<!--- Convert results to array --->
<cfloop index="i" from="1" to="#data.RecordCount#">
<cfset result[i][1]=data.problem_id[i]>
<cfset result[i][2]=data.problem_list[i]>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
<!--- Get art by media type --->
<cffunction name="getdescription" access="remote" returnType="array">
<cfargument name="device_id" type="numeric" required="true">
<cfargument name="problem_id" type="numeric" required="true">
<!--- Define variables --->
<cfset var data="">
<cfset var result=ArrayNew(2)>
<cfset var i=0>
<!--- Get data --->
<cfquery name="data" datasource="#dsn#">
SELECT description_id, description_list, device_id
FROM pa_description
WHERE device_id = #ARGUMENTS.device_id# and problem_id =#arguments.problem_id#
ORDER BY description_list
</cfquery>
<!--- Convert results to array --->
<cfloop index="i" from="1" to="#data.RecordCount#">
<cfset result[i][1]=data.description_id[i]>
<cfset result[i][2]=data.description_list[i]>
</cfloop>
<!--- And return it --->
<cfreturn result>
</cffunction>
CFM
<cfform>
<table>
<tr>
<td>Select Device Type:</td>
<td><cfselect name="device"
bind="cfc:art.getDevice()"
bindonload="true" /></td>
</tr>
<tr>
<td>Select Problem Type:</td>
<td><cfselect name="problem"
bind="cfc:art.getProblem({device})"
bindonload="false" /></td>
</tr>
<tr>
<td>Select Description Type:</td>
<td><cfselect name="description"
bind="cfc:art.getdescription({device},{problem})"
bindonload="false"/></td>
</tr>
</table>

How to create a paging function with MySQL and ColdFusion

I'm trying to create pagination for search results using MySQL and ColdFusion. My intention is to only retrieve the queries that can be displayed on a single page, thus making the process efficient. I tried using two queries in my function, but I could not return two variables to the cfinvoke.
The following code does not paginate, but it displays the result search results using a CFC:
<!---DEFINE DEFAULT STATE--->
<cfparam name="variables.searchResponse" default="">
<cfparam name="URL.titleName" default="">
<cfparam name="URL.genreID" default="">
<cfparam name="URL.platformID" default="">
<!---TitleName can only be blank if one or both genre and platform are selected--->
<cfif StructKeyExists(URL, "searchQuery") AND (Len(Trim(URL.titleName)) LTE 2 AND Len(URL.genreID) IS 0 AND Len(URL.platformID) IS 0)>
<cfset variables.searchResponse = "invalidString">
<cfelseif StructKeyExists(URL, "searchQuery")>
<cfinvoke component="gz.cfcomp.test" method="searchGames" returnvariable="resultData" argumentcollection="#URL#">
<cfset variables.searchResponse = "hasResult">
</cfif>
<cfif searchResponse EQ "hasResult" AND resultData.RecordCount EQ 0>
<cfset variables.searchResponse = "noResult">
</cfif>
Using this logic, I can display what I need to display on the page:
<cfif searchResponse EQ "invalidString">
<cfoutput>Invalid search</cfoutput>
</cfif>
<cfif searchResponse EQ "noResult">
<cfoutput>No results found</cfoutput>
</cfif>
<cfif searchResponse EQ "hasResult">
<cfoutput>Display Results</cfoutput>
</cfif>
If I were executing the queries on the same page, it would be easy to follow the many tutorials out there. But the queries are executing in a function. Displaying the data is easy, but paginating it has become a nightmare for me. Here is my function:
<cffunction name="searchGames" access="public" output="false">
<cfargument name="titleName" required="no" type="string">
<cfargument name="genreID" required="no" type="string">
<cfargument name="platformID" required="no" type="string">
<!--- DEFINE LOCAL VARIABLES--->
<cfset var resultData = "">
<!---GET DATA--->
<cfquery name="resultData" datasource="myDSN">
SELECT *
<!---JOINS FOR GENRE/PLATFORM GO HERE--->
WHERE
<!---CONDITIONS GO HERE--->
</cfquery>
<!---RETURN VARIABLE--->
<cfreturn resultData>
</cffunction>
To paginate, I thought about modifying my function to the following (a new query using a count statement):
<!--- DEFINE LOCAL VARIABLES--->
<cfset var resultCount = "">
<!---GET DATA--->
<cfquery name="resultCount" datasource="myDSN">
SELECT COUNT(gameID) AS rowsFound FROM GAMES
<!---JOINS FOR GENRE/PLATFORM GO HERE--->
WHERE
<!---CONDITIONS GO HERE--->
</cfquery>
<!---RETURN VARIABLE--->
<cfreturn resultCount>
Then I figured if there is a result to return, I would execute a nested query and create the pagination variables:
<cfif resultCount.rowsFound GTE 0>
<cfparam name="pageNumber" default="1">
<cfset var recordsPerPage = 5>
<cfset var numberOfPages = Int(resultCount.RecordCount / recordsPerPage)>
<cfset var recordsToSkip = pageNumber * recordsPerPage - recordsPerPage>
<!---DEFINE LOCAL VARIABLE--->
<cfset var resultData = "">
<cfquery name="resultData" datasource="myDSN">
<!---GET DATA AND SEND IT BACK USING LIMIT WITH #recordsToSkip# and #RecordsPerPage#--->
</cfquery>
<!---RETURN VARIABLE--->
<cfreturn resultData>
</cffunction>
I figured I would return two variables: resultCount and resultData. I would use #resultCount# to build my pagination, and #resultData# to display the output. The problem is I can't return two variables in the same cfinvoke tag. Any ideas of how to approach the the right way? I'm totally lost as to the logic I need to follow.
EDIT: I'm using the following code to paginate now (the only problem is now I have to repass all the search filters back into the URL because using #CGI.SCRIPT_NAME# clears them):
<cfif searchResponse EQ "hasResult">
<!---BASICALLY, IF resultCount.rowsFound is not 0, execute this method--->
<cfinvoke component="gz.cfcomp.test" method="getResult" returnvariable="resultData" argumentcollection="#URL#">
<cfif URL.currentPage IS 1>
--
<cfelse>
Prev Page
</cfif>
<cfif URL.currentPage * recordsPerPage LT resultCount.rowsFound>
Next Page
<cfelse>
--
</cfif>
</cfif>
If your results is not huge, you can stay with the same SQL that returns everything and use
<cfoutput query="data" startrow="#url.start#" maxrows="#recordsPerPage#">
when you display it, see: http://www.coldfusionjedi.com/index.cfm/2006/4/24/ColdFusion-and-Pagination. No Query of Query is needed.
To answer your question
The problem is I can't return two
variables in the same cfinvoke tag.
WHY do you want to return two variables in the same cfinvoke? Instead, write 2 functions: countResult() and getResultData(page, RecordsPerPage)
<cffunction name="countResult" output="false" returntype="numeric">
<cfset var resultCount = "">
<cfquery name="resultCount" datasource="myDSN">
SELECT COUNT(gameID) AS rowsFound FROM GAMES
<!---JOINS FOR GENRE/PLATFORM GO HERE--->
WHERE
<!---CONDITIONS GO HERE--->
</cfquery>
<cfreturn resultCount.rowsFound>
</cffunction>
For getResultData(page, RecordsPerPage) using true paging in DB level:
If you want to do true pagnation in DB level, use LIMIT and OFFSET in MySQL.
<cffunction name="getResultData" output="false" returntype="Query">
<cfargument name="page" type="numeric" default="1">
<cfargument name="recordsPerPage" type="numeric" default="5">
<cfset var resultData = "">
<cfset var offset = (page-1) * RecordsPerPage>
<cfquery name="resultData" datasource="myDSN">
SELECT * LIMIT #recordsPerPage# OFFSET #offset#
<!---JOINS FOR GENRE/PLATFORM GO HERE--->
WHERE
<!---CONDITIONS GO HERE--->
</cfquery>
<cfreturn resultData>
</cffunction>
To figure out how many pages there are:
totalNumOfPages = ceiling(countResult() / recordsPerPage);
Any other question?
Rather than have two functions and two database calls, I've done it like this before (not in MySQL however):
<cffunction name="getResultData" output="false" returntype="Query">
<cfargument name="page" type="numeric" default="1">
<cfargument name="recordsPerPage" type="numeric" default="5">
<cfset var resultData = "">
<cfset var offset = (page-1) * RecordsPerPage>
<cfquery name="resultData" datasource="myDSN">
SELECT *,
(
SELECT COUNT(gameID) AS rowsFound
FROM
<!---JOINS FOR GENRE/PLATFORM GO HERE--->
WHERE
<!---CONDITIONS GO HERE--->
) AS rowsFound
LIMIT #recordsPerPage# OFFSET #offset#
<!---JOINS FOR GENRE/PLATFORM GO HERE--->
WHERE
<!---CONDITIONS GO HERE--->
</cfquery>
<cfreturn resultData>
</cffunction>
It adds a column to the returned recordset called 'rowsFound'.
Not very normalized, but not a big deal. Might be worth it to minimize the DB hits.
I think it's referred to as 'subquery as a scalar operand':
http://dev.mysql.com/doc/refman/5.1/en/scalar-subqueries.html
Tony

how to get Exception details from CFC file

I have a Function in CFC file, which will be called from .cfm file like below
<cffunction name="cftest" access="public" returntype="query" output="true" hint="Function returns Records">
<cfquery name="qryTest" datasource="DBTest">
select * from emp_tab;
</cfquery>
<cfreturn selectRecordsResultSet />
</cffunction>
How can I handle DB Exception using cftry? as this is returning Query, Is it possible to catch the DB Exception and pass the Details to the other page from where it is called?
Thanks
Here is an example of my usual implementation of this:
<cffunction name="getCurrentRecordsCount" access="public" output="false" returntype="any" hint="Get total history records count">
<cfargument name="filters" type="struct" required="false" default="#StructNew()#" hint="Filtering rules">
<cfset var qGetRecordCount = "" />
<cftry>
<cfquery datasource="#variables.dsn#" name="qGetRecordCount">
SELECT COUNT(*) AS cnt FROM ....
</cfquery>
<cfreturn qGetRecordCount.cnt />
<cfcatch type="any">
<cfreturn error(cfcatch.message, cfcatch.detail) />
</cfcatch>
</cftry>
</cffunction>
If you want to handle only database errors, change type to the database.
Errors handling and reporting performed using these three methods:
<cffunction name="error" access="private" output="false" returntype="boolean" hint="Set error status and message">
<cfargument name="message" type="string" required="true" hint="Error message text" />
<cfargument name="detail" type="string" required="false" default="" hint="Error detail text" />
<cfset variables.fError = true />
<cfset variables.fErrorText = arguments.message />
<cfif Len(arguments.detail)>
<cfset variables.fErrorText = variables.fErrorText & " [" & arguments.detail & "]" />
</cfif>
<cfreturn false />
</cffunction>
<cffunction name="gotError" access="public" output="false" returntype="boolean" hint="Return latest error flag state">
<cfreturn variables.fError />
</cffunction>
<cffunction name="getError" access="public" output="false" returntype="string" hint="Return latest error text and reset the flag">
<cfset var txt = variables.fErrorText />
<cfset variables.fError = false />
<cfset variables.fErrorText = "" />
<cfreturn txt />
</cffunction>
Please note that for methods with returntype="void" I use cfset instead of cfreturn:
<cfset error(cfcatch.message, cfcatch.detail) />
So in code I can do the following (cfscript):
// calculate filtered records count
totalLogCount = request.loggingService.getCurrentRecordsCount(filters);
// check if error was thrown
if (request.loggingService.gotError()) {
// report the error details somehow
WriteOutput(request.loggingService.getError());
}