I need to pass a structure to a method, but it will not always be defined.
Is there something like this that would work?
<cfparam name="system_message" default={}>
When I try this I get, the argument passed to the function is not of type struct.
Also, I realize, I could do this:
<cfif ! isdefined("system_message")>
<cfset system_message = {}>
</cfif>
But I was just wondering if there was a shorter way of doing it, using cfparam.
Thanks for any help!
What about:
<cfparam name="system_message" default="#StructNew()#">
CF8 doesn't like the curly braces version.
You're close. You'll need to write it as:
<cfparam name="system_message" default="#{}#">
If you are passing this to a method, you should consider using <cfargument> within a <cffunction> call rather than the more global <cfparam>. The same "default" attribute applies. Then you know your variable exists only within the ARGUMENT scope within the function, better encapsulation!
<cfargument name="system_message" default="#structNew()#">
Related
I wrote the following function:
<cffunction name="check_session_valid" returntype="boolean">
<cfif NOT StructKeyExists(session,"username") OR (len(session.username) EQ 0)>
<script>location.href = 'logout.cfm'</script>
<cfabort>
</cfif>
<cfset session.myApp_start = now()>
<cfreturn true>
</cffunction>
In my .cfm page, I can call that function using
<cfset session_valid = application.lib.check_session_valid()>
OR
#application.lib.check_session_valid()#
What's the difference? Best practice?
Since you asked about best practice, which is a matter of opinion, I think you can improve your function by having it returning either true or false depending on whether or not session.username exists and has a length greater than 0. Then you can use it like this:
<cfif application.lib.check_session_valid()>
code for this condition
<cfelse>
<cflocation href = "logout.cfm">
<!--- note that cfabort is not necessary --->
<cfif>
Regarding your specific question, I think the extra variable, session_valid, is a waste of typing. However, that is simply my opinion.
Not related to your question, I found it curious that you would direct users to a page called logout.cfm. Often users are directed to a page that allows them to log in.
To be honest, both are valid and both would be considered best practice depending on what you are trying to do.
My rule of thumb is if I will need to use the result of a function call more than once, I will set it to a variable
myResult = application.lib.check_session_valid();
If I will only need to use the variable once I would do what Dan mentioned
if( application.lib.check_session_valid() ){
// Do stuff
}
The difference between the examples you showed are
<cfset session_valid = application.lib.check_session_valid()>
This will set the variable named session_valid to whatever is returned from the call to check_session_valid().
#application.lib.check_session_valid()#
This will, in .cfm pages, simply render the value returned from the call to check_session_valid() assuming it is inside of a <cfoutput> tag. There are other places this would also render the value, such as inside a <cfsavecontent>.
I saw this code example in a recording and wanted to know what the colon syntax did. I searched the docs, but I wasn't able to find any info on it:
weather.subscribe(observer: application.observers.currentConditions);
I know we can use colon in CF9 for ternary operators:
result = (condition) ? true : false;
But in this case it looks like it's being used to provide named arguments; so what's it doing there?
<cfset result = obj.func(arg:value,thing:42) /> I looked at this and
went blink, blink... That can't be right! You can't use colons for
named arguments! Er, can you? Well, apparently you can.
http://corfield.org/blog/post.cfm/Learn_something_new_every_day_named_arguments
Yes, you are allowed to use both. I think it's a matter of preference. You can even mix.
Try this and see, mocked up some test function:
<cffunction name="testFunction" returntype="void" hint="I just spit out the arguments I get">
<cfdump var="#arguments#" label="arguments">
</cffunction>
<cfset testFunction(arg1:"hello",arg2:"world") />
<cfset testFunction(arg1="hello",arg2="world") />
<cfset testFunction(arg1:"I can mix",arg2="my named argument syntax") />
Personally, I prefer = for named arguments. You might also notice that if you use IntelliJ IDEA for your ColdFusion development that they do not recognize the colon syntax, so for better parsing you would want to use the = syntax. I can't speak for other IDEs
Looks like a typo to me. In ColdFusion you would use an equals sign (=) not a colon to used named arguments.
Your example would become:
weather.subscribe(observer = application.observers.currentConditions);
I am using some vendor code that we have neither control over nor have the ability to view the source.
For the sake of explanation lets assume I have three files.
Test.cfm
VendorModule.cfm
Custom.cfm
In Test.cfm: I am calling the cfmodule as such:
<CFMODULE template="VendorModule.cfm">
I know nothing about the Vendor module; not the code it executes or the other modules it calls etc, EXCEPT that it will at some point call my file: Custom.cfm
In Custom.cfm: Does some logic and prints Yes or No. (Obviously the variable "something" gets defined in code I am omitting)
<cfif something is true>
<cfoutput>Yes</cfoutput>
<cfelse>
<cfoutput>No</cfoutput>
</cfif>
When I run Test.cfm I get the Yes or No output as expected on the page.
What I want to do is have Custom.cfm set a variable that can then be read by Test.cfm.
So Custom.cfm would look like:
<cfif something is true>
<cfset ??? = "Yes">
<cfelse>
<cfset ??? = "No">
</cfif>
And Test.cfm would look like:
<CFMODULE template="VendorModule.cfm">
<!--- Do something here based on value of ??? set in Custom.cfm --->
Can this be done give what I have available to me?
Thanks!
It's definitely not an ideal situation, but given the fact that you can't see the vendormodule.cfm code let alone change it, your choices are limited.
If vendormodule.cfm calls your custom.cfm in either a cfinclude or a cfmodule you could modify the scope you're after like this, it feels bad to even suggest it, but it should accomplish what you're after.
<cfif something is true>
<cfset caller.caller.result = "Yes">
<cfelse>
<cfset caller.caller.result = "No">
</cfif>
The above code would have to be modified depending at what level custom.cfm is called. For example, if custom.cfm was six levels deep, then it would reference the variables scope within test.cfm like this:
caller.caller.caller.caller.caller.caller.result
Your only other option would be to modify some other shared scope within custom.cfm that test.cfm can see.
Hope that helps.
Usually a cfmodule would return values by using the Caller scope, which is the caller of the cfmodule i.e. Test.cfm. So maybe before and after you called the cfmodule, dump the Variables scope and see if there are any additional vars set by the cfmodule.
Some cfmodule may return vars in other scopes as well, e.g. Request scope. So if you don't seen anything new set in Variables scope, try all other scopes.
How Can I get the URL parameter & Value in Coldfusion?
for Ex:-
my URL is
test.cfm?par1=val1&par2=val2&par3=val3
Is it possible to get the second parameter and its value directly?
with <cfset param='#url.par2#'> I can get value of par2,
But my parameters are dynamicically generated from other page and passed to here (par2 may be next time abc2,xyz2 etc..)
So I think better way is to get the parameter and Value in 2nd Possition(Possition dont change always).
Any Idea How can I get it ?
Thanks in advance
You can also access the url scope as a struct, so you could get:
<cfset param2 = url['param2'] />
This is useful if you might have a naming convention for a bunch of fields. Say you're collecting names and emails like so:
email1=foo#bar.com&name1=Fred&email2=xxx#yyy.com&name2=Sally
You could write something like:
<cfloop condition="someCondition">
<cfset email = url['email' & i] />
<cfset name = url['name' & i] />
<!--- Do something --->
<cfset i++ />
</cfloop>
<cfset Param2 = ListGetAt(CGI.QUERY_STRING,2,"&")>
Order of query string variables is not relevant, or your app shouldnt expect it to be relevant. I think your best bet is to have another variable which is a list of the variables in the order. Like so:
test.cfm?par1=val1&par2=val2&par3=val3&list=var1,var2,var3
Notice the presence of the new variable "list".
So you first grab the value of "list" and then takes it 2nd entry "var2" and reference that in the URL scope. You could easily abstract all of this so the names of the variables themselves dont matter. Good error handling will be necessary to guard against missing expectations.
to get the list of params you can use structKeyList(url) or structKeyArray(url) then access those parameters through the url scope like #url['par1']#
<cfset params = structKeyList(url) />
<cfdump label="parameters" var="#params#" />
<cfloop index="ix" list="#params#">
<cfoutput><div>#ix# = #url[ix]#</div></cfoutput>
</cfloop>
as others have mentioned, you really shouldn't rely on the order of parameters
<cfscript>
par2=getToken(cgi.query_string,2,"&"); // contains "par2=val2"
par2name=getToken(par2,1,"="); // contains "par2"
par2value=urlDecode(getToken(par2,2,"=")); // contains "val2"
</cfscript>
You could also use the listGetAt function, which is basically equivalent to getToken, with slightly different syntax.
By default, ColdFusion passes simple types (like numeric, string, and GUID) by value to functions. I'd like to pass a simple type by reference.
I'm currently wrapping a simple value in a struct (they get passed by reference). This solves my problem but it is very ugly:
<!--- TheFunctionName---->
<cffunction name="TheFunctionName">
<cfargument name="OutVariable" type="struct">
<cfset OutVariable.ID = 5>
</cffunction>
<cfset OutVariable=StructNew()>
<cfset TheFunctionName(OutVariable)>
<!--- I want this to output 5--->
<cfoutput>#OutVariable.ID#</cfoutput>
I'd rather something like this:
<!--- TheFunctionName---->
<cffunction name="TheFunctionName">
<cfargument name="OutVariable" passbyref="true">
<cfset OutVariable = 5>
</cffunction>
<cfset TheFunctionName(OutVariable)>
<!--- I want this to output 5--->
<cfoutput>#OutVariable#</cfoutput>
AFAIK, there's no way to pass simple values by reference in ColdFusion. The only workaround I can think of is the one you're already using.
Instead, I would suggest trying to restructure your program to work with the grain of the language. In cases where there's only one simple value to "modify", you could just make your function return the new value, and call it like:
<cfset SomeVar = TheFunctionName(SomeVar)>
In cases where you're modifying multiple values, take a step back and think about whether it's possible to bundle those multiple values up into a CFC with your mutator functions becoming methods of the CFC. This could be clearer and more maintainable solution anyway.
You can arrange for the variables used outside and inside the function to be in a scope that exists in both code areas. For example, if you put a variable in the "session" or the "request" scope you will be able to access it from within the function. The changes made will persist.
Note that when you are doing this you aren't actually "passing" the variables to the function. The function just assumes the variable exists or creates it, depending on how you code it.
<cffunction name="TheFunctionName">
<cfset Request.StrVar = "inside function<br />" />
</cffunction>
<cfscript>
Request.StrVar = "outside function<br />";
WriteOutput(Request.StrVar);
TheFunctionName();
WriteOutput(Request.StrVar);
</cfscript>
About ColdFusion Scopes
If there is any doubt about the calling page declaring the variable in advance when it is required you'll have to do some legwork with the <cfparam> tag or IsDefined() function.
If you:
declare the function inside of a CFC
invoke the function using <cfinvoke>
You would be able to specify the <cfinvoke> parameter "returnvariable", and then output that variable however you like.
<cfinvoke component="this" method="TheFunctionName" returnvariable="blah">
<cfinvokeargument name="data" value="whatever" type="string">
<cfreturn data>
</cfinvoke>
<cfdump var="#blah#">
If you are writing everything in cfscript, then I would go with what SurroundedByFish said.