How to run a cfquery within the onRequestStart() function? - coldfusion

I want to run a query on every page that a user requests. This query is needed to get back preferences set by the user's organisation for the application. This is what I have tried:
<cffunction name="onRequestStart" access="public" returntype="boolean">
<cfargument type="String" name="TargetPage" required="true"/>
<cfquery name="rsSettings">
SELECT *
FROM
dbo.Settings
</cfquery>
<cfreturn true>
</cffunction>
</component>
However each pages that looks for the rsSettings recordset says that its not defined. If I put the same query within each page that needs it then it works fine.
Does onRequestStart() not handle cfquery?

<cfquery name="request.rsSettings">
SELECT *
FROM
dbo.Settings
</cfquery>
Then in the page use:
request.rsSettings.columName

Related

Coldfusion and Related Selects

For several years, I have used dropdowns to determine the requirements for a SQL query and have never needed to use dropdowns with ‘related selects’ until now. I found a sample on how to do this and I can create related selects (with static data) which works well, see below using my data relating to vehicles and makes.
However, when I come to apply this to my environment, and use dynamic SQL query data, I cannot get it to work. I am confident that all the pieces of code are working together (as it works perfectly for the static data), but if I try and build the data automatically and then create the string to pass to the queryNew function, it fails.
This is the working Content.cfc file with STATIC query strings/data array
<cfcomponent>
<cfset tblMake = queryNew("name,id", "varchar,varchar", [{name:'RENAULT',id:'RENAULT'},{name:'RENAULT',id:'RENAULT'},{name:'RENAULT',id:'RENAULT'}])>
<cfset tblModel = queryNew("name,code,continent_id", "varchar,varchar,varchar", [{name:"TSERIES",code:"TSERIES",continent_id:"RENAULT"},{name:"MASTER",code:"MASTER",continent_id:"RENAULT"}])>
<cfset tblVoltage = queryNew("name,code", "varchar,varchar", [{name:"24 volt",code:"MASTER"},{name:"12 volt",code:"TSERIES"}])>
<cffunction name="getContent" access="remote" returntype="query" output="true">
<cfargument name="strTableName" type="string" required="true">
<cfargument name="strID" type="string" required="true">
<cfargument name="strName" type="string" required="true">
<cfargument name="intDistinct" type="numeric" required="false" default="0">
<cfargument name="selectedCol" type="string" required="false" default="0">
<cfargument name="selectedID" type="string" required="false" default="0">
<cfquery name="qryContent" dbtype="query">
select
<cfif arguments.intDistinct eq 1>distinct</cfif>
#arguments.strID# as theID,
#arguments.strName# as theValue
from #arguments.strTableName#
<cfif arguments.selectedID neq 0>
where #arguments.selectedCol# = '#arguments.selectedID#'
</cfif>
order by #arguments.strName#
</cfquery>
<cfreturn qryContent />
</cffunction>
</cfcomponent>
When I try and create the query using this method, nothing appears to work. I am looping through the database (from a query) and then creating a string using the required structure and the passing it to the function.
<cfcomponent>
<cfset XSTATIC = ""/>
<cfquery name="NOXVehicleModels" datasource="EBSNOX" >
SELECT DISTINCT VehicleMake
FROM [dbo].[NOX-Master]
WHERE VehicleMake IS NOT NULL
</cfquery>
<cfloop query="NOXVehicleModels">
<cfset XSTATIC = XSTATIC & "{name:'" & #Trim(NOXVehicleModels.VehicleMake)# & "',id:'"& #Trim(NOXVehicleModels.VehicleMake)# & "'},"/>
</cfloop>
<cfset XLEN=LEN(#XSTATIC#)/>
<cfset XSTATIC = MID(XSTATIC,1,XLEN-1)/>
<cfoutput>#XSTATIC#</cfoutput>
<cfset tblMake = queryNew("name,id", "varchar,varchar", [#XSTATIC#])>
I have created a separate cfoutput to test the string for structure etc and it appears to be correct, but it is just not passing to the querynew function. This is what the output looks like:-
{name:'CUMMINS',id:'CUMMINS'},{name:'DAF',id:'DAF'},{name:'IVECO',id:'IVECO'},{name:'MAN',id:'MAN'},{name:'MERCEDES',id:'MERCEDES'},{name:'RENAULT',id:'RENAULT'},{name:'SCANIA',id:'SCANIA'},{name:'VOLVO',id:'VOLVO'}
{name:'RENAULT',id:'RENAULT'},{name:'RENAULT',id:'RENAULT'},{name:'RENAULT',id:'RENAULT'}
What I have done so far:-
I have checked that the quotes are not important i.e. single/double.
I have tried to build the query outside of the tag.
Again, all appears (?) to be in order and I cannot understand that could be the problem. Possibly the structure is missing something before parsed to the function?
Any help would be greatly appreciated.
Thanks,
Jack

How to display <cfreturn> output from a CFC being called from a form

I have a web form which uses the action attribute to call a CFC like this:
<form action="mycfc.cfc?method=registeruser">
The CFC processes the data in the form and then I want it to return a variable telling the user if their form submission has been successful or not.
So within my CFC, I put this:
<cffunction name="registeruser" access="remote" hint="registers a new user" returnformat="JSON">
... PROCESSES FORM HERE THEN...
<cfset Msg = 'Success'>
<cfreturn Msg>
<cflocation url = "/registrationpage.cfm">
</cffunction>
How do I display the Msg variable in the registrationpage.cfm page? My output is set to JSON so I guess I have to DeSerialize but I have no idea how to actually reference/access this output from the method.
My whole answer is for educationnal purposes only and I strongly advise you to use an existing framework rather than reinventing the Wheels. Have a look at Picking a ColdFusion MVC Framework
You can store the value in the session scope. A lot of frameworks does it using a flash memory concept, which is a temporary memory (implemented as a struct) that destroys members when accessed.
Have a look at http://cfwheels.org/docs/1-1/chapter/using-the-flash it's quite straight forward to implement an API that does this.
Client code could look like (depending on your implementation):
<cfset session.flash.set('importantMsg', 'some important msg')>
<cflocation ...>
Then from the other page:
<cfif session.flash.has('importantMsg')>
<!--- The following line should also destroy the 'importantMsg' key --->
#session.flash.get('importantMsg')#
</cfif>
Here's an implementation example (not that the implementation is not thread-safe):
FlashMemory.cfc
<cfcomponent>
<cffunction name="init" returntype="FlashMemory">
<cfset variables.instance = {flash = {}}>
</cffunction>
<cffunction name="set" returntype="void">
<cfargument name="key" type="string" required="true">
<cfargument name="value" type="any" required="true">
<cfset variables.instance.flash[arguments.key] = arguments.value>
</cffunction>
<cffunction name="get" returntype="any">
<cfargument name="key" type="string" required="true">
<cfset var v = variables.instance.flash[arguments.key]>
<cfset structDelete(variables.instance.flash, arguments.key)>
<cfreturn v>
</cffunction>
<cffunction name="has" returntype="boolean">
<cfargument name="key" type="string" required="true">
<cfreturn structKeyExists(variables.instance.flash, arguments.key)>
</cffunction>
</cfcomponent>
onSessionStart
<cfset session.flash = new FlashMemory()>
Also please note that in your case, your remote CFC methods shouldn't return anything. You will use the flash memory to pass data around instead. That means when the method has finished it's work you can simply redirect the client.
You probably shouldn't use remote CFC methods in this particular case:
I have never really used remote CFC methods as stateful web services. The various advantages of remote CFC methods like their ability to spit out data in multiple data-interchange formats (JSON, WDDX...) is lost with your implementation.
You could simply do something like:
registration.cfm
<cfset errors = session.flash.get('registrationErrors')>
<cfif arrayLen(errors)>
<!--- Display errors --->
</cfif>
<form method="post" action="register.cfm">
...
</form>
register.cfm
<cfset registration = new Registration(argumentcollection = form)>
<cfset validator = new RegistrationValidator(registration)>
<cfif validator.validate()>
<cfset application.userService.register(registration)>
<!--- You can also redirect to a page that represents the correct state --->
<cflocation url="registered.cfm" addtoken="no">
<cfelse>
<!--- Store the errors collection in the flash memory --->
<cfset session.flash.set('registrationErrors', validator.errors())>
<!--- Redirect to the page the user came from --->
<cflocation url="#cgi.http_referer#" addtoken="no">
</cfif>
Take the cflocation tag out of your function. It will not execute anyway because it's after the cfreturn tag.
Also, post your form to a .cfm page, not the .cfc. From that .cfm page, do this:
<cfset MyObject = CreateObject(stuff for your cfc goes here)>
<cfset MessageFromFunction = MyObject.registeruser()>
<cfoutput>#MessageFromFunction</cfoutput.

Using Onclick on an HREF tag in Coldfusion

I was wondering if someone can tell me what is wrong with this code because it doesn't work and it does not give me any errors either.
With this code I am trying to access a component that updates a DB table each time someone press on the link.
<cfajaxproxy bind="url:http://www.example.com/admin/CRM/linktracking.cfc" />
<cfscript>
SSLtype = (CGI.HTTPS EQ 'off')?'http://':'https://';
</cfscript>
<cfset domainname = CGI.SERVER_NAME>
<cfset domainURL = SSLtype&CGI.SERVER_NAME&CGI.SCRIPT_NAME&'?'&CGI.QUERY_STRING>
<script>
function insertTracking(href) {
var instance = new ajaxjsclass();
instance.setCallbackHandler();
<cfoutput>
instance.insertTrack(href,'#surveymain.contactid#','#domainname#','#domainURL#');
</cfoutput>
}
</script>
This is the component I am trying to access.
<cfcomponent>
<cffunction name="insertTrack" access="remote" returntype="void" >
<cfargument name="clickedURL" required="yes">
<cfargument name="contactid" required="yes">
<cfargument name="domainName" required="yes">
<cfargument name="domainURL" required="yes">
<cfquery name="q_inserttrack" datasource="dpsigweb">
update survey_tracking
set surveystarted = <cfqueryparam value="#now()#" cfsqltype="CF_SQL_TIMESTAMP">
where contactid= '#contactid#'
</cfquery>
<cfif ARGUMENTS.contactid NEQ ''>
<cfscript>
additionalInfo = '<b>Clicked URL</b> - <i>#ARGUMENTS.clickedURL#</i><br><br><b>From Site</b> - <i>#ARGUMENTS.domainURL#</i>';
gaCFC = CreateObject("component","mod_sigweb.components.guestaccount");
gaCFC.AddCorrespondenceCurDoctorProcedureRemote(
functionPassword = 'password',
contactid = '#ARGUMENTS.contactid#',
theMessage = additionalInfo,
statustype = 'Survey Started',
contactresult ='Survey Started'
);
</cfscript>
</cfif>
</cffunction>
</cfcomponent>
This is where I am trying to access the function from:
<a href="http://dev.example.com/surveys/survey.cfm?id=#id#&contactid=#contactid#&doctorid=#doctorid#" onClick="insertTracking(this.href)" >
I am suspecting that my <cfajaxproxy> tag may have a syntax error but when I am pressing the link I am not getting any errors.
I change my cfajaxproxy to this
<cfajaxproxy cfc="linktracking" jsclassname="ajaxjsclass" />
but still the function does not seem to work. I even moved the component and the cfm file in the same folder but it still doesn't work.
Edit:
I forgot to mention that i am sending this code in an email template. i don't know if that matters in any way. I created a test page that I am testing locally and my code works just fine. If there is something that I need to change because of that please let me know
Solution Found. I put the update query code directly on the page so when the user presses on the link and opens the page the query runs from there instead from the link.
The above code is working fine I just had issues with it because I was sending the page via email to the user. At least this is what I suspect.

Is there a way to (per request) set a non-persistent Database Bean in Coldbox

I am looking to migrate from a custom framework to Coldbox.
The application has 3 datasources
Core
Common
Site
The Core datasource stores information about the sites, the common datasource stores shared information, like the states table, and the Site datasource stores the data relevant to the website.
The Site datasource is changed per request based on the URL of the request, allowing each site to be sandboxed into its own database.
From my testing it seems that the DatasourceBeans generated by Coldbox and used in it's autowiring are stored/cached in the application scope. This is what I'm thinking to do, but the change to the datasource is persisted across requests.
In Coldbox.cfc
datasources = {
Core = {name="DSNCore", dbType="mssql", username="", password=""},
Common = {name="DSNCommon", dbType="mssql", username="", password=""},
Site = {name="", dbType="mssql", username="", password=""}
};
and
interceptors = [{
class="interceptors.Website",
properties={}
}];
Interceptor named Website.cfc
<cfcomponent name="Website" output="false" autowire="true">
<cfproperty name="dsncore" inject="coldbox:datasource:Core">
<cfproperty name="dsn" inject="coldbox:datasource:Site">
<cffunction name="Configure" access="public" returntype="void" output="false" >
</cffunction>
<cffunction name="preProcess" access="public" returntype="void" output="false" >
<cfargument name="event" required="true" type="coldbox.system.web.context.RequestContext">
<cfargument name="interceptData" required="true" type="struct">
<cfset var q="" />
<cfdump var="#dsn.getMemento()#" label="DSN before change" />
<cfquery name="q" datasource="#dsncore.getName()#">
SELECT
Datasource
FROM
Websites
WHERE
Domain = <cfqueryparam cfsqltype="cf_sql_idstamp" value="#cgi.http_host#" />
</cfquery>
<cfscript>
dsn.setName(q.Datasource);
</cfscript>
<cfdump var="#dsn.getMemento()#" label="DSN after change" />
<cfdump var="#q#" label="Results of query" /><cfabort />
</cffunction>
</cfcomponent>
Is there any way to do this in a way that I can use the Coldbox autowire datasource beans?
Honestly, this is just the way I thought I would do it, if anyone has any other ideas on how to get my model to use NON-hardcoded different datasource per request, I would love to understand the framework better.
This question also extends to ORMs. Is there a way for, say, Transfer to use a different datasource per request? What if the databases can have potentially different schemas? Lets say that one database has been updated to a newer version, but another still uses an older version and I essentially have some if statements in the code to provide enhanced functionality to the updated database.
You may be reading these questions and thinking to yourself "You shouldn't do that." Well i am, so please no answers saying not to do it. If you have ideas on better ways to have single codebase attached to different databases then I'm all ears though.
Another way you could do it is by using the requestStartHandler in Coldbox.cfc
<!---config/Coldbox.cfc--->
requestStartHandler = "Main.onRequestStart"
<!---handlers/Main.cfc--->
<cffunction name="onRequestStart" returntype="void" output="false">
<cfargument name="event" required="true">
<cfquery name="q" datasource="#dsncore.getName()#">
SELECT
Datasource
FROM
Websites
WHERE
Domain = <cfqueryparam cfsqltype="cf_sql_idstamp" value="#cgi.http_host#" />
</cfquery>
<cfset rc.dataSource = q.Datasource />
</cffunction>
Then you just have your dataSource stored in the Request Collection becuase onRequestStart will fire on every Request.

Putting data into the application scope

For my lookup tables, the ones that are the same for every user in the application, I do an
Application.objectname = createobject(...).init(datasource)
in the init method, I read the table into the this scope like so:
cfquery name="this.queryname"
return this
Now, whenever I need to reference the query, I can refer to it like this:
cfselect query="Application.objectname.queryname" ...
Q: Is there anything wrong with that?
No, that would be fine. The server will keep the entire object instance in memory as part of the application scope, which will include all of its properties.
As a question of style, I would suggest making your query a private property (in the variables scope in a CFC) rather than a public one (in a CFC's this scope). Allowing an object property to be public implies that as the black box designer, you're okay with an unknown developer overwriting the value. If these are database lookup tables you're storing, I'm guessing you intend this data to be read-only. Consider the following:
<cfcomponent hint="Proxy for database lookup tables" output="false">
<cfproperty name="variables.lookupTable1" type="query" hint="[Private] lookupTable1 query object." />
<cfproperty name="variables.lookupTable2" type="query" hint="[Private] lookupTable2 query object." />
<!--- Implicit initialization --->
<cfscript>
variables.lookupTable1 = QueryNew('');
variables.lookupTable2 = QueryNew('');
</cfscript>
<!--- Active initialization --->
<cffunction name="init" returntype="void" access="public" hint="Initializes the query objects with data." output="false">
<cfargument name="dsn" type="string" required="true" hint="The datasource to use." />
<cfquery name="variables.lookupTable1" datasource="#arguments.dsn#">
SELECT * FROM [TblFoo]
</cfquery>
<cfquery name="variables.lookupTable2" datasource="#arguments.dsn#">
SELECT * FROM [TblBar]
</cfquery>
</cffunction>
<!--- Data Fetching Methods --->
<cffunction name="getFoo" returntype="query" access="public" hint="Returns the contents of TblFoo." output="false">
<cfreturn variables.lookupTable1 />
</cffunction>
<cffunction name="getBar" returntype="query" access="public" hint="Returns the contents of TblFoo." output="false">
<cfreturn variables.lookupTable2 />
</cffunction>
</cfcomponent>
Syntactically, no. However, I'm assuming that you've also included a "name" attribute to that cfselect tag, since it's required.
If that's the only place you use the query object, you might want to cache the output of the cfselect drop-down box instead. :)
If you're not setting application scope variables in onApplicationStart() or onServerStart(), then don't forget to use <cflock>