I have two cfinvoke, I need to use them in one cfm
<cfinvoke component="cfc/queries" method="getProjects" searchString="#Session.Auth.pref_name#" view="#Session.Auth.view#" returnvariable="Projects">
<cfinvoke component="cfc/queries" method="projectDetails" searchString="#URL.id#" projectsuffix="#URL.suffix#" returnvariable="Details">
to return two queries, but when I coding like this way it's not working.
I'm still new to the ColdFusion and I don't know how to fix that.
Since both functions are in the same CFC, you wouldn't want to use cfinvoke since it recreates the object each time it's called. Instead, use a new or a createObject().
<cfset queries = new location.to.cfc.queriesCFC()>
Then you can just reference the functions.
<cfset Projects =
queries.getProjects(
searchString=session.Auth.pref_name,
view = session.Auth.view
)
>
<cfset Details =
queries.projectDetails(
searchString=url.id,
projectsuffix=url.suffix
)
>
You may want to sanitize url.id and url.suffix before you pass them through. This will help with injection issues.
What does getProjects() do?
We can write like as below,
<!--- Object creation --->
<cfset query = CreateObject("component", "cfc.queries")/>
<!--- Function call --->
<cfset Projects = query.getProjects( searchString = session.Auth.pref_name, view = session.Auth.view )>
<cfset Details = query.projectDetails( searchString = session.Auth.pref_name, view = session.Auth.view )>
Related
I am using OpenBD and trying to check whether bucket exists or not on my S3 server, if it is not exist then, to create new bucket. Here's my code:
index.cfm
<cfset request.awsaccess = "zzzzawsaccesszzzz">
<cfset request.awskey = "zzzzzzzzawskeyzzzzzzzz">
<cfset request.datasource="tcs">
<cfset request.region="us-west-2">
<cfscript>
AmazonRegisterdatasource(datasource=request.datasource,awsaccess=request.awsaccess,awskey=request.awskey,region=request.region );
result = AmazonS3listbuckets( datasource=request.datasource );
WriteDump(result);
WriteOutput(result.bucket[1]);
</cfscript>
For the above code I am getting this output:
Now I am adding one more function AmazonS3createbucket(),
<cfscript>
result = AmazonS3createbucket( datasource=request.datasource, bucket="anyBucket" );
</cfscript>
For the above script I am getting error: that No such function exists - amazons3createbucket.. Here's the screenshot:
I am referring the OpenBD Manual to filter these function.
Also faced the same problem while using this functions also:
<cfscript>
result = AmazonS3bucketexists( datasource=request.datasource, bucket="anyBucket" );
</cfscript>
Have you tried using an alternate syntax?
<cfscript>
result = AmazonS3bucketexists(ArgumentCollection = {
datasource : request.datasource,
bucket : "anyBucket"
});
</cfscript>
If I have
<cfset arr_arguments = ["a","b","c"]>
<cfunction name="someFunction">
<cfargument name="someArgumentOne">
<cfargument name="someArgumentTwo">
<cfargument name="someArgumentThree">
</cffunction>
Is there any way to invoke someFunction with the arguments arr_arguments, similar to someFunction("a","b","c")? I of course know I can use argumentCollection to pass a (keyed) structure to a function, but I am specifically asking about passing in an (keyless) array. In JS this could be easily done with someFunction.apply(this,arr_arguments), but in coldfusion I just can't find any way of doing this.
Unnamed arguments are passed into a function as a structure with numeric keys matching the positions of the arguments defined in the function arguments. So instead of passing the named arguments, you can convert your array to a struct with numeric keys, and then pass the struct in with argumentCollection:
<cfset arr_arguments = {"1"="a","2"="b","3"="c"}>
<cfset someFunction(argumentCollection=arr_arguments)>
You can easily convert an array to a struct with numeric keys like this:
<cfset args = {}>
<cfloop from="1" to="#arrayLen(arr_arguments)#" index="i">
<cfset args[i] = arr_arguments[i]>
</cfloop>
In Coldfusion 10, you can use the invoke function to do this. It appears to be an undocumented way to pass an array of arguments in order. Example in cfscript:
invoke('', 'someFunction', ['a','b','c']);
The first argument of invoke is the component name (or an empty string for UDFs). The second is the function name, and third is an argument array. Note that both component and function names must be passed as strings.
I tested this both with and without defined argument names and the order of arguments was preserved.
Ok if you are looking to call a function with an arbitary array of arguments the best option I can suggest is to make a wrapper function. See below example code
<cfscript>
function testMe(
required string arg1,
required string arg2
) {
writedump(arguments);
}
function invokeFunction(
required inFunction,
required array functionArguments
) {
var stcArguments = {};
// Introspect to get the method parameters
var arrFunctionArguments = GetMetaData(arguments.inFunction).parameters;
// Now figure out what we are iterating to
var numArgumentsToParse = Min(ArrayLen(arguments.functionArguments),ArrayLen(arrFunctionArguments));
// Populate the arguments structure
for (var i=1;i<=numArgumentsToParse;i++) {
stcArguments[arrFunctionArguments[i].name] = arguments.functionArguments[i];
}
// And try to call it
return arguments.inFunction(
argumentCollection = stcArguments
);
}
invokeFunction(testMe,[1,2]); // Works fine
invokeFunction(testMe,[1,2,3,4]); // Just skips 3 and 4
// invokeFunction(testMe,[1]); // Errors due to not enough arguments
// invokeFunction(fakeFunctionName,[1]); // Errors due to undefined function
</cfscript>
This will of course error if either of the following happen
One or more arguments are not of correct type
The function doesn't actually exist
Not all required arguments are passed in
But you can handle that outside. This will use introspection to figure out the argument names, populate the structure accordingly, and call the requested function.
Here's a UDF which will do that for you:
<cfscript>
function test_function(a,b,c){
writeOutput("arguments.a"&arguments.a&"<br>");
writeOutput("arguments.b"&arguments.b&"<br>");
writeOutput("arguments.c"&arguments.c&"<br>");
return arguments.a & "::" & arguments.b & "::" & arguments.c;
}
function invoke_positional(func,arr){
var metaData = GetMetaData(func);
var args={};
for(var pos=1;pos<=ArrayLen(arr);pos++){
args[metaData.parameters[pos].name]=arr[pos];
}
return arguments.func(argumentCollection=args);
}
data = ["StringOne","StringTwo",22];
result = invoke_positional(test_function,data);
</cfscript>
<cfoutput>
<p>
Returned: #result#
</p>
</cfoutput>
invoke_positional works by using the UDF's metadata to build up a named set of parameters and then uses them with argumentCollection=
So, one option I have found out to work is to create an arguments object separately from the actual real arguments scope and simply add the values to it using a numeric index.
<cfset args_input = createObject("java","com.naryx.tagfusion.cfm.engine.cfArgStructData").init()>
<cfloop from="1" to="#arraylen(arr_arguments)#" index="i">
<cfset args_input[i] = arr_arguments[i]>
</cfloop>
<cfset someFunction(argumentCollection=args_input)>
If you encounter this situation using adobe coldfusion I would advice you to try
<cfset correctClass = arguments.getClass().getName()>
To get the string to pass to the createObject. Or if that doesn't work just have a dedicated function to return an empty arguments object
<cfset args_input = this.generateArgumentObject()>
and
<cffunction name="generateArgumentObject">
<cfreturn arguments>
</cffunction>
Oh well, all I know for sure is that it will work for sure in OpenBD and based on a post I saw in the past on Ben Nadel's blog I am pretty sure this should work in Adobe ColdFusion as well.
I'm attempting to build a method call from strings that have been passed into an object that refer to another object.
normally when calling an object we write the code like this:
application.stObj.oNewsBusiness.getNews(argumentCollection=local.stArgs);
However what I have done is created an array that contains the object name, the method name and the argument collection.
<cfscript>
local.stArgs = {};
local.stArgs.nNewsID = 19;
local.stArgs.sAuthor = "John";
local.aData = [];
local.aData[1] = local.stArgs;
local.aData[2] = "stObj.oNewsBusiness";
local.aData[3] = "getNews";
</cfscript>
however i am struggling to recombine all this to be a method call.
UPDATE using suggestion but still with issue
While cfinvoke seems to work for:
<cfinvoke component="#application.stObj.oNewsBusiness#" method="#local.sMethod#" argumentcollection="#local.stArgs#" returnvariable="local.qData"></cfinvoke>
it doesn't work when doing something like:
<cfscript>
local.stArgs = local.aData[1];
local.sObject = local.aData[2];
local.sMethod = local.aData[3];
</cfscript>
<cfinvoke component="application.#local.sObject#" method="#local.sMethod#" argumentCollection="#local.stArgs#" returnvariable="local.qData"></cfinvoke>
it generates an error:
Could not find the ColdFusion component or interface application.stObj.oNewsBusiness
CFInvoke is generally used to handle dynamic method calls.
http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WSc3ff6d0ea77859461172e0811cbec22c24-7e0a.html
CFInvoke has an argumentcollection attribute so you can pass your arguments in the way you are used to.
Dan is correct CFInvoke is the way to go
<cfinvoke component="#mycomponentname#" method="get" arg1="#arg1#" arg2="#arg2#" arg3=..>
<cfinvoke component="application.#local.sObject#" method="#local.sMethod#"argumentCollection="#local.stArgs#" returnvariable="local.qData"></cfinvoke>
from your update won't work because there are no # signs around the component variable.
You could do
<cfset local.componentName = "application." & local.sObject>
<cfinvoke component="#local.componentName#" method="#local.sMethod#"argumentCollection="#local.stArgs#" returnvariable="local.qData"></cfinvoke>
There's probably an inline way of combining application. with the variable on the cfinvoke call, but I don't know off the top of my head.
Edit: Dan Wilson's comment does it better in an inline way.
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>
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>