Problem:
I have 2 files in the same folder:
index.cfm
application.cfc
My index.cfm is empty and below is my application.cfc code:
<cfcomponent>
<cffunction name="onApplicationStart">
<cfapplication name="TimeHistory" clientmanagement="No" sessionmanagement="No" setclientcookies="Yes" />
<cflock scope="application" type="exclusive" timeout=10>
<cfset application.test = "xxxxxxxxxxxxxx">
</cflock>
<cfreturn true />
</cffunction>
<cffunction name="onRequestStart">
<cfdump var="#application#" label="before">
<cfif structKeyExists(URL, "restart")>
<cfset onApplicationStart()>
</cfif>
<cfdump var="#application#" label="after">
<cfabort>
</cffunction>
</cfcomponent>
As you can see, I am setting a test variable in the application scope.
When I call the page with the ?restart URL query param, this is the output:
url: ../index.cfm?restart
Next,I removed the ?reset URL query param and loaded the page, this is the output:
url: ../index.cfm
Question:
Should it not have set the variable as part of the first call and then be persisted for future calls? How can I make it do so?
Based on +Leigh's comment , I modified my code which now works. Leaving this here as a solution. Thank you Leigh :)
<cfcomponent>
<cfscript>
this.name = "TimeHistory";
this.clientmanagement= "no";
this.sessionmanagement = "no";
this.setClientCookies = "no";
this.setDomainCookies = "no";
</cfscript>
<cffunction name="onApplicationStart">
<cflock scope="application" type="exclusive" timeout=10>
<cfset application.test = "xxxxxxxxxxxxxx">
</cflock>
<cfreturn true />
</cffunction>
<cffunction name="onRequestStart">
<cfdump var="#application#" label="before">
<cfif structKeyExists(URL, "restart")>
<cfset onApplicationStart()>
</cfif>
<cfdump var="#application#" label="after">
<cfabort>
</cffunction>
</cfcomponent>
My function calls the SendGrid API. It returns an Array + structure. I'm writing a function to return a CFQuery dataset.
Goal
I want to pass a deserialized data object to my function and get a query dataset.
Here is my working code and the output:
<cfparam name="variables.ddata" default="#structnew()#">
<!--- API Call Code here --->
<cfset arr = DESerializeJSON(returnStruct.Filecontent) />
<cfdump var="#arr#">
My code:
<cfset arrayit(arrobj= arr) >
<cfdump var="#variables.ddata#" >
<cffunction name="arrayit" access="public" returntype="void">
<cfargument name="arrobj" type="array" required="yes">
<cfset var arr=arguments.arrobj />
<cfloop from="1" to = "#arrayLen(arr)#" index="i">
<cfif isValid("string", arr[i])>
<cfset StructInsert(variables.ddata, i, arr[i]) />
</cfif>
<cfif isstruct(arr[i])>
<cfset structit(structobj = arr[i]) />
</cfif>
</cfloop>
</cffunction>
<cffunction name="structit" access="public" returntype="void" output="yes">
<cfargument name="structobj" type="any" required="yes">
<cfset stru = arguments.structobj />
<cfloop collection="#stru#" item="S">
<cfif isValid("string", stru[S])>
<cfset StructInsert( variables.ddata, S, stru[S]) />
</cfif>
<cfif isarray(stru[S])>
<cfset arrayit(arrobj = stru[S]) >
</cfif>
</cfloop>
</cffunction>
Result:
When I add this line in my function
<cfif isstruct(stru[S])>
<cfset variables.ddata = arrayit(arrobj = stru[S]) />
</cfif>
An error occurs:
Element type is undefined in a CFML structure referenced as part of an
expression. The error occurred on line 71.
** Full Code**
<cfsavecontent variable="returnStruct.Filecontent">
[{"date":"2016-04-05","stats":[{"type":"category","name":"5","metrics":{"blocks":1,"bounce_drops":0,"bounces":9,"clicks":4,"deferred":1,"delivered":1,"invalid_emails":8,"opens":4,"processed":1,"requests":1,"spam_report_drops":0,"spam_reports":1,"unique_clicks":3,"unique_opens":3,"unsubscribe_drops":0,"unsubscribes":9}}]}]
</cfsavecontent>
<cfset arr = DESerializeJSON(returnStruct.Filecontent) />
<cfloop from="1" to="#arrayLen(arr)#" index="i">
<cfif isValid("string", arr[i])>
<cfset StructInsert(variables.ddata, i, arr[i],true ) />
</cfif>
<cfif isstruct(arr[i])>
<cfsavecontent variable="rr">
<cfdump var="#arr[i]#" label="Line 48 ERROR" >
</cfsavecontent>
<cfset NotifyErrorAdmin(emailBody = "#rr#" ,emailsubject = "Line 48") />
<cfset structit(structobj = arr[i]) />
</cfif>
<cfif isarray(arr[i])>
<cfsavecontent variable="rr">
<cfdump var="#arr[i]#" label="Line 54 ERROR" >
</cfsavecontent>
<cfset NotifyErrorAdmin(emailBody = "#rr#" ,emailsubject = "Line 54") />
<cfset arrayit(arrobj = arr[i]) >
</cfif>
</cfloop>
</cffunction>
<cffunction name = "structit" access="public" returntype="void" output="yes">
<cfargument name = "structobj" type="any" required="yes">
<cfset stru = arguments.structobj />
<cfloop collection="#stru#" item="S">
<cfif isValid("string", stru[S])>
<cfset StructInsert( variables.ddata, S, stru[S],true) />
</cfif>
<cfif isarray(stru[S])>
<cfsavecontent variable="rr">
<cfdump var="#stru[S]#" label="Line 86 ERROR" >
</cfsavecontent>
<cfset NotifyErrorAdmin(emailBody = "#rr#" ,emailsubject = "Line 87") />
<cfset arrayit(arrobj = stru[S]) >
</cfif>
<cfif isstruct(stru[S])>
<cfsavecontent variable="rr">
<cfdump var="#stru[S]#" label="Line 97 ERROR" >
</cfsavecontent>
<cfset NotifyErrorAdmin(emailBody = "#rr#" ,emailsubject = "Line 97") />
<cfset structit(structobj = stru[S]) />
</cfif>
</cfloop>
</cffunction>
ERROR
Your UDF arrayit accepts an argument of type array but when that condition is true then a struct is being passed so, the error.
i.e.,
<cfif isStruct(stru[S])>
<!--- This means stru[S] is a struct --->
<cfset variables.ddata = arrayit(arrobj = stru[S])>
<!--- arrObj should be of type 'array' --->
</cfif>
So, it should be:
<cfif isStruct(stru[S])>
<cfset variables.ddata = structit(structobj = stru[S])>
</cfif>
But, the error for this case will be different than that you have added.
Additionally,
StructInsert() takes an optional argument allowoverwrite which is by default false and according to docs:
if key exists and allowoverwrite = "False", ColdFusion throws an
exception.
I just did it! :) just wanted to share my project with you guys also hope it will help someone else also...
Request if you guys find anything you feel I can improve please share.
Special Thanks for response on my post. #Beginner & #Leigh
API Call Json Return: 1
<cfsavecontent variable="returnStruct.Filecontent">
[{"date":"2016-04-05","stats":[{"type":"category","name":"5","metrics":{"blocks":1,"bounce_drops":0,"bounces":9,"clicks":4,"deferred":1,"delivered":1,"invalid_emails":8,"opens":4,"processed":1,"requests":1,"spam_report_drops":0,"spam_reports":1,"unique_clicks":3,"unique_opens":3,"unsubscribe_drops":0,"unsubscribes":9}}]}]
</cfsavecontent>
<cfset arr = DESerializeJSON(returnStruct.Filecontent) />
CFC : 2
<cfcomponent>
<cfparam name="variables.qryclsvar" default="" type="any"/>
<cfparam name="variables.qryclsvarfg" default="true" type="any"/>
<cffunction name="APItoquery" access="public" returntype="any">
<cfargument name = "APIobj" type="any" required="yes">
<cfset var vAPIobj = arguments.APIobj />
<cfset var APIDATA = structnew() />
<cfset var APIDATAqr = "" />
<cftry>
<cfloop from="1" to="#arrayLen(vAPIobj)#" index="jj">
<cfif isarray(vAPIobj[jj])>
<cfset APIDATA = arrayit(structobj = vAPIobj[jj] ,datastruct = APIDATA) />
</cfif>
<cfif isstruct(vAPIobj[jj])>
<cfset APIDATA = structit(structobj = vAPIobj[jj],datastruct = APIDATA) />
</cfif>
<cfif NOT StructIsEmpty(APIDATA)>
<!--- Add in query object --->
<cfset APIDATAqr = structtoquery(structobj= APIDATA) />
<cfelse>
<cfset APIDATAqr ="NO Data Found!" />
</cfif>
</cfloop>
<cfcatch>
<cfdump var="#cfcatch#" label="APItoquery">
</cfcatch>
</cftry>
<cfreturn APIDATAqr>
</cffunction>
<cffunction name = "arrayit" access="public" returntype="any">
<cfargument name = "arrobj" type="any" required="yes">
<cfargument name = "datastruct" type="any" required="yes" >
<cfset var arr = arguments.arrobj />
<cfset var arrdata = arguments.datastruct />
<cftry>
<cfloop from="1" to="#arrayLen(arr)#" index="i">
<cfif ArrayContains(arr, i) >
<cfset StructInsert(arrdata, i, arr[i],true ) />
</cfif>
<cfif isarray(arr[i])>
<cfset arrdata = arrayit(arrobj = arr[i] ,datastruct = arrdata) >
</cfif>
<cfif isstruct(arr[i]) >
<cfset stdata = structit(structobj = arr[i],datastruct = arrdata) />
</cfif>
</cfloop>
<cfcatch>
<cfdump var="#cfcatch#" label="arrayit">
</cfcatch>
</cftry>
<cfreturn arrdata>
</cffunction>
<cffunction name = "structit" access="public" returntype="any" output="yes">
<cfargument name = "structobj" type="any" required="yes">
<cfargument name = "datastruct" type="any" required="yes">
<cfset var stru = arguments.structobj />
<cfset var stdata = arguments.datastruct />
<cftry>
<cfloop collection="#stru#" item="S">
<cfif isarray(stru[S])>
<cfset stdata = arrayit(arrobj = stru[S] ,datastruct = stdata) >
<cfelseif isstruct(stru[S]) >
<cfset stdata = structit(structobj = stru[S],datastruct = stdata) />
<cfelse>
<cfset StructInsert( stdata, S, stru[S],true) />
</cfif>
</cfloop>
<cfcatch>
<cfdump var="#cfcatch#" label="structit">
</cfcatch>
</cftry>
<cfreturn stdata>
</cffunction>
<cffunction name = "structtoquery" access="public" returntype="any" output="yes">
<cfargument name = "structobj" type="any" required="yes">
<cfset var vstructobj = arguments.structobj />
<cfset var cols = StructKeyList(vstructobj)>
<cfset var colstyp = "">
<cftry>
<cfif variables.qryclsvarfg EQ true>
<cfloop from="1" to="#listlen(cols,',')#" index="L">
<cfset colstyp = ListAppend(colstyp,"VarChar",",")>
</cfloop>
<!--- Create a new query. --->
<cfset variables.qryclsvar = queryNew(
'#cols#',
'#colstyp#'
)>
<cfset variables.qryclsvarfg = false>
</cfif>
<cfset QueryAddRow(variables.qryclsvar, 1)>
<cfloop collection="#vstructobj#" item="sd">
<cfset QuerySetCell(variables.qryclsvar, "#sd#", vstructobj[sd])>
</cfloop>
<cfcatch>
<cfdump var="#cfcatch#" label="structit">
</cfcatch>
</cftry>
<cfreturn variables.qryclsvar>
</cffunction>
</cfcomponent>
CFM : 3
<cfset sgObj = createobject("component","cfc.mycfc") />
<cfset mystruct = sgObj.APItoquery(APIobj= arr1) >
<cfdump var="#mystruct#" label="mystruct">
MA ! ....
I am trying to run 3 threads of an http request in ColdFusion. This is an email system which will select 3 different campaigns and send there respective recipients at the same time.
But my following code only runs one thread and then drops off.
<cfscript>
newsLetterCampaignGateway = createObject("component", "legacy.ssl.admin.news.model.newsLetterCampaignGateway");
newsLetterList = newsLetterCampaignGateway.getNewsLettersDueForSend();
//writedump(newsLetterList);abort;
</cfscript>
<cfloop query="newsLetterList" >
<cfset newsLetterId = newsLetterList.newsletterid>
<cfset campId = newsLetterList.id>
<cfset fromEmail = newsLetterList.fromEmail>
<!--- <cfdump var="#campId#"> --->
<cfthread action="run" name="runCampaign#campId#" >
<cflock
name="runCampaign_#campId#_Lock"
type="exclusive"
timeout="60">
<!--- <cfdump var="#campId#"> --->
<cfscript>
httpService = new http();
httpService.setMethod("get");
httpService.setCharset("utf-8");
httpService.setUrl("http://mysamplesite.com/legacy/ssl/admin/news/model/newsLettercampaign.cfc");
httpService.addParam(type="url",name="method",value="sendCampaignNewsLetters");
httpService.addParam(type="url",name="live",value="true");
httpService.addParam(type="url",name="campaignId",value="#campId#");
httpService.addParam(type="url",name="newsLetterId",value="#newsLetterId#");
httpService.addParam(type="url",name="fromEmail",value="#fromemail#");
httpService.send();
</cfscript>
</cflock>
</cfthread>
</cfloop>
<cfloop query="newsLetterList" >
<cfthread
action="join"
name="runCampaign#campId#"
/>
</cfloop>
Any idea guys?
Well, I decided to not use the cfhttp and used the threading like this and it works.
<cfsetting requesttimeout="300000">
<cfscript>
newsLetterCampaignGateway = createObject("component", "path.to.cfc");
newsLetterList = newsLetterCampaignGateway.getNewsLettersDueForSend();
</cfscript>
<cfloop query="newsLetterList" >
<cftry>
<cfthread action="run" name="runCampaign#url.campaignId#" >
<cfset sendRequest(url)>
</cfthread>
<cfcatch>
<cfdump var="#cfcatch#"><cfabort>
</cfcatch>
</cftry>
</cfloop>
<cffunction name="sendRequest">
<cfargument name="urlStu" required="true">
<cfset newsLettercampaign = createObject("component", "path.to.cfc")>
<cfset newsLettercampaign.sendCampaignNewsLetters(arguments.urlStu)>
</cffunction>
I was wondering if there is any strings centralization in ColdFusion similar to Android strings.xml file.
So, that my code remains intact if i want to do any changes in string
ColdFusion is a programming language, not a user interface framework.
There is nothing like Android's string resource management built into ColdFusion, but it would be very easy to implement that yourself.
resources/strings.xml:
<!-- keep as a structure with unique element names -->
<strings>
<heading>This is a test.</heading>
<greetings>
<hello>Hello World!</hello>
<bye>Goodbye World!</bye>
</greetings>
</strings>
ColdFusion utility function (for example in a CFC component util.cfc):
<cffunction name="ReadResouceXml" returntype="struct" access="public" output="no">
<cfargument name="path" type="string" required="yes">
<!--- internal use argument --->
<cfargument name="xmlDoc" type="xml" required="no">
<cfset var xmlElem = "">
<cfset var output = StructNew()>
<!--- read XML file from disk --->
<cfif not StructKeyExists(arguments, "xmlDoc")>
<cffile action="read" file="#ExpandPath(path)#" variable="xmlDoc" charset="UTF-8">
<cfset xmlDoc = XmlParse(xmlDoc).XmlRoot>
</cfif>
<!--- recursively convert XML to a CF struct --->
<cfloop index="i" from="1" to="#ArrayLen(xmlDoc.XmlChildren)#">
<cfset xmlElem = xmlDoc.XmlChildren[i]>
<cfif ArrayLen(xmlElem.XmlChildren) gt 0>
<cfset output[xmlElem.XmlName] = ReadResouceXml("", xmlElem)>
<cfelse>
<cfset output[xmlElem.XmlName] = xmlElem.XmlText>
</cfif>
</cfloop>
<cfreturn output>
</cffunction>
Coldfusion usage:
<cfobject type="component" name="util" component="util">
<cfset strings = util.ReadResouceXml("resources/strings.xml")>
<cfoutput>
<h1>#strings.heading#</h1>
#strings.greetings.hello# - #strings.greetings.bye#
</cfoutput>
I am trying to convert some pages from my app to use cfc's, and one page uses a stored procedure to retrieve a couple sets of data.
Now when I access the results, they act just like a if I used a <cfquery> tag, and all of the functionality that gives. So now I am trying to use this same stored procedure in a cfc that I am building, and I would like to be able access the results in the same manner, and there in lies my problem. I'm not sure how to return multiple queries from the function, without creating an array, which I have started. By the way, the function is incomplete. I was just trying to get something to work. In the below setup I get an array of query objects, but I feel there is a better way to do it.
Here is the <cffuntion>:
<cffunction name="getProfileData"
access="public"
output="false"
returntype="string">
<cfargument name="cusip" type="string" required="true">
<cfargument name="report_date" type="date" required="true">
<cfset var errorMessage = "everything is good">
<cftry>
<cfstoredproc datasource="#dsn#" procedure="prc_asset_profile_retrieve">
<cfprocparam type="in" cfsqltype="cf_sql_varchar" value="#cusip#" dbvarname="#cusip">
<cfprocparam type="in" cfsqltype="cf_sql_varchar" value="#report_date#" dbvarname="#reportDate">
<cfprocresult name="profile_head" resultset="1">
<cfprocresult name="attribution" resultset="2">
<cfprocresult name="characteristics" resultset="3">
<cfprocresult name="exposure" resultset="4">
<cfprocresult name="weights" resultset="5">
<cfprocresult name="holdings" resultset="6">
</cfstoredproc>
<cfset var profileArray = []>
<cfset #ArrayAppend(profileArray,profile_head)#>
<cfcatch type="any">
<cfset errorMessage = "something happened">
</cfcatch>
</cftry>
<cfreturn profileArray>
</cffunction>
When I output some test data, it matches up
<cfset count = fund_profile.getProfileData("#cusip#","#report_date#")>
<cfdump var="#count[1]#">
<cfoutput>
From cfc (##count[1].recordCount##): #count[1].recordCount#<br>
From stored proc (##profile_head.recordCount##): #profile_head.recordCount#
</cfoutput>
I get:
From cfc (#count[1].recordCount#): 1
From stored proc (#profile_head.recordCount#): 1
But the second way looks so much cleaner.
-----------------------------WORKING SOLUTION------------------------------
So after working with the answer from #leigh, I came up with this.
Here is the full cfc:
<cfcomponent displayname="Fund Profile" hint="This is the cfc that will do the processing of all fund profile information" output="false">
<cfproperty name = "result1"> <!--- PROFILE HEAD --->
<cfproperty name = "result2"> <!--- ATTRIBUTION --->
<cfproperty name = "result3"> <!--- CHARACTERISTICS --->
<cfproperty name = "result4"> <!--- EXPOSURE --->
<cfproperty name = "result5"> <!--- WEIGHTS --->
<cfproperty name = "result6"> <!--- HOLDINGS --->
<cffunction name="init"
displayname="init"
hint="This will initialize the object"
access="public"
output="false"
returnType="Any">
<cfargument name="dsn" type="string" required="true" />
<cfargument name="cusip" type="string" required="true" />
<cfargument name="report_date" type="date" required="true" />
<cfset variables.dsn = #arguments.dsn#>
<cfset variables.cusip = #arguments.cusip#>
<cfset variables.report_date = #arguments.report_date#>
<cfscript>
getProfiledata(cusip,report_date);
</cfscript>
<cfreturn this>
</cffunction>
<cffunction name="getProfileData"
access="private"
output="false"
returntype="void">
<cfargument name="cusip" type="string" required="true">
<cfargument name="report_date" type="date" required="true">
<cfstoredproc datasource="#dsn#" procedure="prc_asset_profile_retrieve">
<!--- STORED PROCEDURE HASN'T CHANGED. SEE ABOVE FOR CODE --->
</cfstoredproc>
<cfscript>
setProfilehead(profile_head);
setAttribution(attribution);
setCharacteristics(characteristics);
setExposure(exposure);
setWeights(weights);
setHoldings(holdings);
</cfscript>
<cfreturn>
</cffunction>
<!--- NOT GOING TO INCLUDE ALL SETTERS AND GETTERS, --->
<!--- BECAUSE THEY ARE ALL THE SAME OTHER THAN THE NAMES --->
<cffunction name="setProfileHead" access="private">
<cfargument name="ProfileHead">
<cfset variables.result1 = arguments.ProfileHead>
</cffunction>
<cffunction name="getProfileHead" access="public" returntype="query">
<cfreturn variables.result1>
</cffunction>
</cfcomponent>
Here is the code from the calling page:
<cfset fund_profile = CreateObject("component", "CFCs.fund_profile").init("#dsn#","#cusip#","#report_date#")>
<cfset profile_head = fund_profile.getProfileHead()>
Sorry for all the code, but I wanted to make the code available. So does anyone see any problems with what I came up with?
A function can only return a single value. If you wish to return multiple values, you will need to use some type of complex object (an array, structure, ...) If arrays are not intuitive enough, you could place the queries in a structure and return that instead. Then the calling page could access the queries by name, rather than index.
(Side note, be sure to properly var scope/localize all function variables.)
<cfset var data = {}>
...
<!--- store query results in structure --->
<cfset data.profile_head = profile_head>
<cfset data.attribution = attribution>
...
<cfset data.holdings = holdings>
<!--- return structure --->
<cfreturn data>
I would create other methods in the CFC that would each be responsible for returning a result from the stored proc. In the main method , call setters
setProfileHead(profilehead:profileHead)
<cffunction name=ProfileHead>
<cfarguments name=ProfileHead />
<cfset variables.profilehead = arguments.profilehead>
</cffunction>
Then...
<cffunction name=GetProfileHead>
<cfreturn variables.profileHead />
</cffuction>