In ColdFusion, how do I determine if a query string variable exists? - coldfusion

In ColdFusion, how can I determine if a variable exists within the querystring without throwing an error attempting to check it?

There are two options.
The first is to use cfparam to define a default value eg:
<cfparam name="url.varname" type="string" default="" />
This ensures that you can always refer to url.varname
The second is to use isDefined or structKeyExists to test for the presence of the variable:
<cfif isDefined("url.varname") and url.varname eq 42> do something </cfif>
or
<cfif structKeyExists(url, "varname") and url.varname eq 42> do something </cfif>

I have used this approach in many places.
At the top of the page:
<cfparam name="request.someVal" default="request.defaultVal">
Later in the page or custom tag, check for the value of the request.someVal variable, without fear of it crashing, since it has a default value.
<cfif ("request.someVal" eq "something")>
...
</cfif>
.
.
.

In <cfscript>, you can
param url.varname; // throws error if it does not exist
param url.varname = ""; // sets value it was not already set

Related

In ColdFusion, what is the difference between setting a function to a variable and calling a function in hashtags?

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>.

Cfparam for a struct

<cfparam name="instance.params.sel_isCriteriaChanged" default="false">
Here instance is a global structure but "sel_isCriteriaChanged" inside that is created using form variable. But in certain form i dont have that variable. It'll be undefined in that case.
So in that case how to set the variable to false as default value.
I am using this variable inside CFC file
If I understand the question correctly you have something like this:
<cfparam name="instance.params.sel_isCriteriaChanged" default="false">
<cfset instance.params = {}>
<cfloop collection="#FORM#" item="key">
<cfset instance.params[key] = FORM[key]>
</cfloop>
but the cfparam gets overwriten here. Just make sure the form value is always defined:
<cfparam name="FORM.sel_isCriteriaChanged" default="false">
If I understand your problem correctly, you might do something like the following:
<cfset instance.params.sel_isCriteriaChanged = structKeyExists(form, "sel_isCriteriaChanged") ? form.sel_isCriteriaChanged : false />
This is shorthand for:
<cfif structKeyExists(form, "sel_isCriteriaChanged")>
<cfset instance.params.sel_isCriteriaChanged = form.sel_isCriteriaChanged />
<cfelse>
<cfset instance.params.sel_isCriteriaChanged = false />
</cfif>
I like the terseness of the ternary operator ? : and I try to avoid using <cfparam> if possible. Hope this helps.

How can I retrieve a (quasi) Array from a URL string?

I would like to achieve something I can easily do in .net.
What I would like to do is pass multiple URL parameters of the same name to build an array of those values.
In other words, I would like to take a URL string like so:
http://www.example.com/Test.cfc?method=myArrayTest&foo=1&foo=2&foo=3
And build an array from the URL parameter "foo".
In .net / C# I can do something like this:
[WebMethod]
myArrayTest(string[] foo)
And that will build a string array from the variable "foo".
What I have done so far is something like this:
<cffunction name="myArrayTest" access="remote" returntype="string">
<cfargument name="foo" type="string" required="yes">
This would output:
1,2,3
I'm not thrilled with that because it's just a comma separated string and I'm afraid that there may be commas passed in the URL (encoded of course) and then if I try to loop over the commas it may be misinterpreted as a separate param.
So, I'm stumped on how to achieve this.
Any ideas??
Thanks in advance!!
Edit: Sergii's method is more versatile. But if you are parsing the current url, and do not need to modify the resulting array, another option is using getPageContext() to extract the parameter from the underlying request. Just be aware of the two quirks noted below.
<!--- note: duplicate forces the map to be case-INsensitive --->
<cfset params = duplicate(getPageContext().getRequest().getParameterMap())>
<cfset quasiArray = []>
<cfif structKeyExists(params, "foo")>
<!--- note: this is not a *true* CF array --->
<!--- you can do most things with it, but you cannot append data to it --->
<cfset quasiArray = params["foo"]>
</cfif>
<cfdump var="#quasiArray#">
Well, if you're OK with parsing the URL, following "raw" method may work for you:
<cffunction name="myArrayTest" access="remote" output="false">
<cfset var local = {} />
<!--- parse raw query --->
<cfset local.args = ListToArray(cgi.QUERY_STRING, "&") />
<!--- grab only foo's values --->
<cfset local.foo = [] />
<cfloop array="#local.args#" index="local.a">
<cfif Left(local.a, 3) EQ "foo">
<cfset ArrayAppend(local.foo, ListLast(local.a, "=")) />
</cfif>
</cfloop>
<cfreturn SerializeJSON(local.foo) />
</cffunction>
I've tested it with this query: ?method=myArrayTest&foo=1&foo=2&foo=3,3, looks to work as expected.
Bonus. Railo's top tip: if you format the query as follows, this array will be created automatically in URL scope ?method=myArrayTest&foo[]=1&foo[]=2&foo[]=3,3.
listToArray( arguments.foo ) should give you what you want.

Coldfusion #iif and sessions

I am buliting an add/edit user form, when the page is accessed there is an if statement which detects if we are editing a current user or addindg a new user by a url id
<cfif isDefined('URL.id')>
<cfquery name="getSquadMember" datasource="#application.datasource#">
SELECT * from squad WHERE id=#URL.id#
</cfquery>
<cfset #name#=#getSquadMember.athlete_name# />
<cfelse>
<cfset SESSION.squad = structNew()>
<cfparam name="SESSION.squad.name" default="">
</cfif>
That is fine but the problem comes in evaluting if the session or value exists, I get an error
<cfinput class="text" name="name" type="text" id="name" value ="#IIf(IsDefined('name'), DE('#name#'), DE("#SESSION.squad.name#"))#" required="yes" />
Element squad.name is undefined in session. Why is it eveluating if session exists when the first condition is met?
Thanks,
R.
First up - if you can possibly avoid using iif(), then do so. It causes many more headaches than it has ever solved...
However, note the following from the ColdFusion documentation on iif():
If a variable is undefined, ColdFusion throws an error when it processes this function. The following example shows this problem:
#IIf(IsDefined("Form.Deliver"), DE(Form.Deliver), DE("no"))# This returns "Error resolving parameter FORM.DELIVER".
To avoid this problem, use the DE and Evaluate functions in code such as the following:
#IIf(IsDefined("Form.Deliver"), Evaluate(DE("Form.Deliver")), DE("no"))# This returns "no"; ColdFusion does not throw an error.
Or, if you're on CF9 (which supports ternary operators):
<cfinput class="text" name="name" type="text" id="name" value ="#IsDefined('name') ? name : SESSION.squad.name#" required="yes" />
Is there a reason as to why you need to have two different variable names for the same item? Could you just do:
<cfif isDefined('URL.id')>
<cfquery name="getSquadMember" datasource="#application.datasource#">
SELECT * from squad WHERE id=<cfqueryparam value="#URL.id#" cfsqltype="CF_SQL_INTEGER" />
</cfquery>
<cfset name= getSquadMember.athlete_name />
<cfelse>
<cfset squad = structNew()>
<cfset name = "" />
</cfif>
<input type="text" name="name" id="name=" value="#name#" />
Which would then in turn remove the need for the iff statement
HTH
J
Couple of issues:
1)
<cfquery name="getSquadMember" datasource="#application.datasource#">
SELECT * from squad WHERE id=#URL.id#
</cfquery>
You're asking for a SQL injection here. Use cfqueryparam
2)
<cfset #name#=#getSquadMember.athlete_name# />
It's your second post where I notice weird use of #
It should be as Jason wrote it:
<cfset name = getSquadMember.athlete_name />
3) As for your question:
<cfinput class="text" name="name" type="text" id="name" value="#IIf(IsDefined('name'), DE('#name#'), DE("#SESSION.squad.name#"))#" required="yes" />
This way is not really readable.
Set some variable for the value first.
Second thing - you don't need to put the variables in DE like this, this will do:
DE(name)
And as you're using cfinput the whole thing probably needs to be correct. And another issue - you don't really need to use cfinput there. Normal HTML input will do and save you some CF parsing.
The problem you are seeing is that IIF() has to evaluate all parts of the statement.
You can't really use it for checking if a variable is defined or not as it will always try to evaluate the contents of both the true and false responses.
As long as you bear this in mind when using IIF() there's no problem with using it. Just keep it simple and don't try to use it when a variable might not exist.
I was going to point out the other issues with your code, but the other answers have already done a good job of covering these.

coldfusion ignore undefined variables

if I use
<cfoutput>#somevariable#</cfoutput>
and somevariable is not defined I get an error, how can I prevent the error from occourring?
is there a simple way of implementing a conditional that doesn't require a bunch of extra lines?
<cfparam name="somevariable" default="" />
If you're on cf 9 you can use a ternary operation, but cfparam is more 'best practicey'.
#isDefined("somevariable") ? somevariable : 'default string'#
You can test for the variable
<cfoutput>
<cfif isDefined("somevariable")>
#somevariable#
<cfelse>
handle default scenario here
</cfif>
</cfoutput>
or you could use inline conditional
<cfoutput>
#IIF(isDefined("somevariable"),de(somevariable),de(""))#
</cfoutput>