ColdFusion: Trying to query database in CFScript - coldfusion

My boss wants me to use cfscript instead of tags for database interaction. Does anybody know of any good tutorials? I bought the Adobe ColdFusion application development book, vol 2. But it does not have much on scripting. I did google and found this site, but it did not explain much.
Does any body know of any good tutorials on accessing the data base in CFScript?
Basically I have to convert the following to using CFScript:
<cfquery name="drafts" datasource="ICEchat">
SELECT * from Messages where IsTemp=1 and LinkA=#FORM.LinkA# and LinkB=#FORM.LinkA#
</cfquery>
<cfif drafts.recordcount GT '0'>
<cfquery name="Attachments" datasource="ICEchat">
SELECT * FROM Attachments where id=2
</cfquery>
{ Message:"<cfoutput query="drafts">#Message#</cfoutput>", Attachments:[<cfoutput query="attachments">
"#url#"<cfif attachments.currentRow LT attachments.recordcount>,</cfif>
</cfoutput>]}
<cfelse>
<cfquery name="addrecord" datasource="ICEchat">
INSERT INTO Messages
VALUES(1,1,' ',1)
</cfquery>
{ Message:"NA", Attachments:[]}
</cfif>

From the 4th link on google for "cfscript query tutorial":
<CFSCRIPT>
myQry = new Query(); // new query object
myQry.setSQL("select bookid, title, genre from app.books where bookid = :bookid"); //set query
myQry.addParam(name="bookid",value="5",CFSQLTYPE="CF_SQL_INTEGER"); // add query param
qryRes = myQry.execute(); // execute query
writedump(qryRes.getResult().recordcount, true); // get resultcount
writedump(qryRes.getResult(), false); // dump result
writeoutput('<BR>');
</CFSCRIPT>
That ought to tell you everything you need to know.
Also, you really should not be creating JSON manually, no matter how simple it is. Use serializeJson().

Didn't test this, but this should do it.
<cfscript>
local.drafts = new Query();
local.drafts.setDatasource("ICEchat");
local.drafts.addParam(name="linkA", value="#form.linkA#", cfsqltype="CF_SQL_VARCHAR");
local.drafts.addParam(name="linkB", value="#form.linkB#", cfsqltype="CF_SQL_VARCHAR");
local.drafts.setSQL("SELECT * from Messages where IsTemp=1 and LinkA = :linkA and LinkB = :linkA");
local.drafts.execute().getResult();
if (local.drafts.recordcount GT 0) {
local.attachments = new Query();
local.attachments.setDatasource("ICEchat");
local.attachments.setSQL("SELECT * FROM Attachments where id=2");
local.attachments.execute().getResult();
WriteOutput("{ Message: ");
for (i=1; i LTE local.drafts.recordcount; i=i+1) {
WriteOutput(local.drafts.message[i]);
}
WriteOutput(", Attachments: ");
for (i=1; i LTE local.attachments.recordcount; i=i+1) {
WriteOutput(local.drafts.url[i]);
if (i LT local.attachments.recordcount) {
WriteOutput(", ");
}
}
WriteOutput("}");
} else {
local.q = new Query();
local.q.setDatasource("ICEchat");
local.q.setSQL("INSERT INTO Messages VALUES(1,1,' ',1)");
local.q.execute();
WriteOutput("{ Message:"NA", Attachments:[]}");
}
</cfscript>

I have been searching for a solution for the same error. I get not defined errors or construct errors. Have been chatting with Ray Camden for the past day, but everything he has suggested is not doing what I need either. I have been working on a website conversion from the standard CF tags to cfscript.
Ray suggested that the .execute(); is what pulls the recordcount and suggested this is all was needed: x=queryExecute(); would fetch the recordcount, with (x) being the query. He suggested not to use getPrefix(); but I read that the .getPrefix(); is what pulls the recordcount. He is experienced I am sure you know Ray Camden, but I keep getting the same error no matter what I try doing inside of my code.

Related

queryEach or .each() not Working at CF 11, Why?

I was seaching in google better form to interate over "query" in coldfusion, since im new in the company im working, and im trying to get more from CF
Here my attempts:
My models:
<cffunction hint="Foo" name="Foo" access="public" returntype="query">
<!--- Argumentos --->
<cfargument hint="Something" name="ArgComBus" type="string" required="no" default="">
<cfargument hint="Other thing" name="ArgPar" type="string" required="no" default="">
<cfscript>
queryService = new Query();
queryService.setSql("
SELECT
column1,
column2,
FROM
tab_bar
WHERE
1=1
#arguments.ArgComBus#
");
queryService.setDataSource(session.Dsn);
if(Len(Trim(arguments.ArgPar))){
Evaluate(arguments.ArgPar);
}
queryResult = queryService.execute();
qBus = queryResult.getResult();
</cfscript>
<cfreturn qBus>
</cffunction>
My script
<cfscript>
arrFoo = arrayNew(1);
qFoo = this.Foo(
ArgComBus = " AND column1 = #variables.bar# ");
// First Attempt - The each method was not found.
qFoo.each(function (foo) {
arrFoo.append(foo);
});
// Second Attempt - Variable QUERYEACH is undefined.
queryEach(qFoo, function (foo) {
arrFoo.append(foo);
});
writeDump(arrFoo);
</cfscript>
My Server Dump
InstallKit Native Windows
appserver Tomcat
productlevel Developer
productname ColdFusion Server
productversion 11,0,05,293506
rootdir C:\CFusion11\cfusion
I even used getMetaData() on my query variable qFoo and that return that is array... so when i tried use something like that (trying to convert array in query?)
cfQuery = createObject("java", "coldfusion.sql.QueryTable").init(qFoo);
.each() and queryEach() same answer... i even tried use arrayEach() but return the object is coldfusion.sql.QueryTable and not array
You are running ColdFusion 11.
The queryEach() function was not added until ColdFusion 2016:
reference 1 (from cfdocs)
reference 2 (from Adobe docs)
Originally I had posted that the each() member function was not available in Adobe ColdFusion 11. Aquitaine pointed out in the comments that it actually is. I incorrectly referenced the Each() function for Lucee that works with collections. The Each() function related to this question is actually the script version of the ArrayEach() tag function. Which is available in ColdFusion 11 (it was actually added in ColdFusion 10). Sorry for the confusion.
The documentation may be wrong. I could not get the function to work as Each() except under ColdFusion 2018. For ColdFusion 11 I could only get it to work as ArrayEach().
reference 3 (from cfdocs)
reference 4 (from Adobe docs)
Here are a couple of examples on how to loop over a query in ColdFusion 11 (borrowed from cfdocs):
// Define our query
platform = ["Adobe ColdFusion", "Railo", "Lucee"];
myQuery = queryNew(" ");
queryAddColumn(myQuery, "platform", "CF_SQL_VARCHAR", platform);
// By row index
for (i = 1; i <= myQuery.recordCount; i++) {
writeOutput("<li>#myQuery["platform"][i]#</li>");
}
// By query
for (row in myQuery) {
writeOutput("<li>#row.platform#</li>");
}
// By arrayeach
writeOutput("<h3>By arrayeach:</h3>");
function printArray(vendor, index)
{
writeOutput("<li>#vendor#</li>");
}
arrayEach(platform,printArray);
I created a gist for you on TryCF.com so you can see this code in action and play around with it if you like. Just click here to run the code.

Adding appointments to your calendar through email notifications

I need an example for adding events to my calendars through emails. For example when I send an email to my clients regarding their appointment, i want them to have an option in the email that will allow them to add the event on their calendar by just clicking on a button or something.
Is there anything out there that someone can direct me to it?
I need this mainly to use it with coldfusion 9.
Thanks!
I'd recommend the iCalUs UDF from http://www.cflib.org/udf/icalus
WebDH provides a great example here.
Here's an example using CF9 that I quickly put together but haven't tested yet.
<cfscript>
eventStr = {};
eventStr.organizerName = "John Doe"; //Organizer Name
eventStr.organizerEmail = "john.doe#email.com"; //Organizer Email
eventStr.startTime = ParseDateTime("12/30/2011 11:00"); //format: m/d/yyyy HH:mm OR h:mm TT -- this is Eastern time
eventStr.subject = "Demo Example";
eventStr.location = "StackOverflow.com";
eventStr.description = "Example iCalendar using CF9";
// Display in browser
//pc = getpagecontext().getresponse();
//pc.getresponse().setcontenttype('text/calendar');
//pc.setHeader("Content-Disposition","inline;filename=newAppointment.ics");
//writeOutput(iCalUS(eventStr));
//Email
m = new mail();
m.setSubject( "Event" );
m.setTo( "user#email.com" );
m.setFrom( "me#email.com" );
m.setServer( "localhost" );
//m.addParam( file="#ACCOUNT_TXT_FILE#" );
m.addPart( type="text", charset="utf-8", wraptext="72", body="Attached is a calendar event..." );
m.addPart( type="text/calendar" body="#iCalUS(eventStr)#");
m.send();
</cfscript>
Here's another example reference that shows how to email the calendar event.
Here is a very basic implementation of the ICS format. This is designed to be accessed through a browser, but it'd be fairly academic to change to create a text file with ICS extension and send it via e-mail.
<cfheader name="Content-Disposition" value="attachment; filename=event.ics" />
<cfcontent reset="true" type="text/calendar" />
<cfscript>
// handle all-day events
if (NOT isDate(starttime) OR NOT isDate(endtime)) {
dtstart=';VALUE=DATE:#dateFormat(eventdate,"yyyymmdd")#';
dtend=';VALUE=DATE:#dateFormat(dateAdd("d",1,eventdate),"yyyymmdd")#';
} else {
dtstart=';TZID="Eastern Standard Time":#dateFormat(eventdate,"yyyymmdd")#T#timeFormat(starttime,"HHmmss")#';
dtend=';TZID="Eastern Standard Time":#dateFormat(eventdate,"yyyymmdd")#T#timeFormat(endtime,"HHmmss")#';
}
</cfscript>
<cfoutput>
BEGIN:VCALENDAR
PRODID:-//Company//Source//EN
VERSION:2.0
METHOD:PUBLISH
BEGIN:VTIMEZONE
TZID:Eastern Standard Time
BEGIN:STANDARD
DTSTART:16011104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
CLASS:PUBLIC
CREATED:#dateFormat(dateAdded,"yyyymmdd")#T#timeFormat(dateAdded,"HHmmss")#Z
DESCRIPTION:#desc#
DTEND#dtend#
DTSTAMP:#dateFormat(dateAdded,"yyyymmdd")#T#timeFormat(dateAdded,"HHmmss")#Z
DTSTART#dtstart#
LAST-MODIFIED:#dateFormat(dateApproved,"yyyymmdd")#T#timeFormat(dateApproved,"HHmmss")#Z
LOCATION:#location#
PRIORITY:5
SEQUENCE:0
SUMMARY;LANGUAGE=en-us:#title#
TRANSP:OPAQUE
UID:#dateFormat(now(),"yyyymmdd")#T#timeFormat(now(),"HHmmss")#Z##uniqueID#
BEGIN:VALARM
TRIGGER:-PT15M
ACTION:DISPLAY
DESCRIPTION:Reminder
END:VALARM
END:VEVENT
END:VCALENDAR
</cfoutput>

cfwheels nested properties issue

I'm trying to get this to work...
I have a five tables that I"m trying to tie together: properties, languages, propertyLanguages, buildings and buildingTranslations
properties,languages and propertylanguages is a typical many-to-many relationship that I have working. What I'm trying to do next is have the buildings, which are linked to the property and have text fields for each language that will go inside the buildingtranslations.
I've setup the foreign keys for the propertylanguages, buildings and buildingtranslations
I'm just not sure how to setup the model and the controller when creating/updating building records
edit
I've managed to create a view in mssql that represents the relationship
hopefully this makes it easier to see the relationships.
I want to create and edit Buildings with the translation fields included (and updated in the database)
The languages are assigned at the property level. The building that is linked to the property through the propertyid uses the languages available (through propertylanguages[where propertyid = building.propertyid]) to determine the buildingTranslations required for the building
Hopefully this helps some:
models/Building.cfc
hasMany(name="BuildingTranslations", foreignKey="yrhBuildingId");
belongsTo(name="Property", foreignKey="yrhPropertyId");
controllers/Buildings.cfc
function new () {
building = model("Building").new();
building.yrhPropertyId = params.yrhPropertyId; //assuming this was passed in
requiredLanguages = model("PropertyLanguages").findAll(where="yhrPropertyId=#building.yhrPropertyId#");
}
function create () {
building = model("Building").new(params.Building);
building.save();
requiredLanguages = model("PropertyLanguages").findAll(where="yhrPropertyId=#building.yhrPropertyId#");
for (var i = 1; i <= requiredLanguages.recordCount; i++)
{
buildingTranslation = model("BuildingTranslation").new();
buildingTranslation.yrhBuildingId = building.id;
buildingTranslation.yrhLanguageId = requiredLanguages.yrhLanguageId[i];
buildingTranslation.langName = params.BuildingTranslations[requiredLanguages.yrhLanguageId[i]];
buildingTranslation.save();
}
redirectTo(action="list");
}
here's the approach I've taken
<cfset viewBuildingNames = model("yrhBuildingNamesView").findAll(where="yrhBuildingId=#params.key#")> <!--- FIND ALL BUILDING NAMES --->
<cfset yrhbuilding = model("Yrhbuilding").findByKey(key=params.key)> <!--- CREATE BUILDING MODEL --->
<cfset yrhproperty = model("YrhProperty").findByKey(key=yrhbuilding.yrhPropertyId, include="YrhPropertyLanguages")> <!--- language info through property--->
<cfset yrhbuilding.yrhproperty = yrhproperty>
<cfset yrhbuilding.yrhBuildingTranslations = ArrayNew(1)>
<cfloop query="viewBuildingNames">
<cfset yrhBuildingTranslation = model("yrhBuildingTranslation").new(yrhBuildingId=#yrhBuildingId#, yrhLanguageId=#yrhLanguageId#, langName=#LANGNAME#)>
<cfset ArrayAppend(yrhbuilding.yrhBuildingTranslations, yrhBuildingTranslation)>
</cfloop>
it's sort of half cfwheels way. it relies on the view created in the database
once the mode; is created, the updating works, but I don't get an error message on the empty langNames, just an error, Which I can live with.
I'm planning on adding another layer of items residing under the building, that will require the same connection to the propertyLanguages, While it should still work OK I'm getting increasingly queasy about the cfwheels magic taking care of these things. I might be switching to handling complex relationships directly.

ColdFusion 9 datasources defaulting to enterprise

I have a ColdFusion instance being run under enterprise, but for some reason it ignores the local data source. It will only connect if I put the data source at the enterprise level.
I've even tried the following code and it only returns the data sources that are declared at the instance manager, not the instance itself.
<cfset factory = createObject("java", "coldfusion.server.ServiceFactory")>
<cfset datasources = factory.getDataSourceService().getDatasources()>
<cfloop collection="#datasources#" item="dsnName">
#dsnName#<br>
</cfloop>
Any help would be greatly appreciated.
These should help you figure out which instance you are on:
<cfscript>
loc = {};
loc.machineName = createObject('java','java.net.InetAddress').localhost.getCanonicalHostName();
loc.machineName2 = createObject('java','java.net.InetAddress').localhost.getHostName();
loc.hostAddress = createObject('java','java.net.InetAddress').localhost.getHostAddress();
loc.instanceName = createObject('java','jrunx.kernel.JRun').getServerName();
writeDump( var: loc );
</cfscript>
If you are having problems getting the datasources you might need to authenticate first with your cf administrator password like so:
createObject('component','CFIDE.adminapi.administrator').login('your-password');
There is a datasourceExists(), verifyDatasource() and getDatasource() method on the data source service that you might find handy:
<cfscript>
loc = {};
loc.dss = createObject('java','coldfusion.server.ServiceFactory').getDataSourceService();
loc.datasources = loc.dss.getDatasources();
loc.exists = loc.dss.datasourceExists('your-dsn');
loc.verified = loc.dss.verifyDatasource('your-dsn');
loc.datasource = loc.dss.getDatasource('your-dsn');
writeDump( var: loc );
</cfscript>

Best practice for Datasource use in a CFC

I have an application which uses context sensitive datasources. Currently I keep the datasource information stored a such
reqeust.DB.Datasource = "DatasourceName";
request.DB.Username = "DatasourceUsername"
request.DB.Password = "DatasourcePassword"
I then overwrite the variables depending on the context, so each cfquery tag has the attributes datasource="#request.DB.Datesource#" ... etc ...
I want to start moving to more CFC centric frameworks like Coldbox, but I just don't see how this would work.
Do I need to pass in a datasource object into the init statement of the CFC? This seems like it would be a super PITA.
With CF9, you can this.datasource in Application.cfc as the default datasource. Unfortunately, it doesn't seem to have a way to set username/password
Either
A.) use an Dependency Injection framework such as ColdSpring (only suitable for singleton Services), Lightwire or Coldbox's own DI solution (Wirebox). and inject the datasource/username/password through the init constructor or setters.
B.) set <Datasources> in Coldbox.xml.cfm, see: http://wiki.coldbox.org/wiki/ConfigurationFile.cfm
<!--Datasource Setup, you can then retreive a datasourceBean
via the getDatasource("name") method: -->
<Datasources>
<Datasource alias="MyDSNAlias"
name="real_dsn_name"
dbtype="mysql"
username=""
password="" />
</Datasources>
Even if your objects only get initialized at request level, it seems like it should be less of a pain to work with in this fashion.
<cfscript>
request.DB.Datasource = "DatasourceName";
request.DB.Username = "DatasourceUsername";
request.DB.Password = "DatasourcePassword";
request.randomDAO = createObject('component','DAOStuff.randomDAO');
request.randomDAO.init(DBObject = request.DB);
request.someQuery = request.randomDAO.someGetter();
request.someOtherQuery = request.randomDAO.someOtherGetter();
request.aThirdQuery = request.randomDAO.aThirdGetter();
</cfscript>
As opposed to:
<cfscript>
request.DB.Datasource = "DatasourceName";
request.DB.Username = "DatasourceUsername";
request.DB.Password = "DatasourcePassword";
</cfscript>
<cfquery name="request.someQuery"
datasource=request.DB.Datasource
username=request.DB.Username
password=request.DB.Password>
--SOME SQL HERE
</cfquery>
<cfquery name="request.someOtherQuery"
datasource=request.DB.Datasource
username=request.DB.Username
password=request.DB.Password>
--SOME SQL HERE
</cfquery>
<cfquery name="request.aThirdQuery"
datasource=request.DB.Datasource
username=request.DB.Username
password=request.DB.Password>
--SOME SQL HERE
</cfquery>
If it is safe for your data objects to exist at an application level (assuming here that the data source for the object will not change at run-time and that you have written thread-safe CFCs) You can store and initialize DAOs at application level and then each request has wonderfully simple code like:
<cfscript>
request.someQuery = application.randomDAO.someGetter();
request.someOtherQuery = application.randomDAO.someOtherGetter();
request.aThirdQuery = application.randomDAO.aThirdGetter();
</cfscript>