Using component as a variable type

This post is more than 2 years old.

Did you know that ColdFusion 8 adds "component" as a valid type to both the returnType attribute of cffunction and the type attribute of cfargument? What does this mean? The default "rule" for types is that if you do not specify a type recognized by ColdFusion (list provided at the bottom of this post), then ColdFusion assumes you mean a component. So for example:

<cfargument name="app" type="apple">

This line means that the app argument must be a component of type apple. It is also valid to pass in a component that extends apple, like greenapple.cfc. But again - what happened here was that ColdFusion didn't recognize apple and therefore considers it the name of a component.

ColdFusion 8 adds to the list of the supported types by adding "component". When you use component, you can pass any CFC instance. In the past, the only way to allow for any CFC would be to use "any". If we wanted to change that app argument to allow any CFC, we could use this:

<cfargument name="app" type="component">

For your reference, here is the list of supported types for cfargument/cffunction.

anyAny variable type is allowed.
arrayAn array is required.
binaryBinary data is required.
booleanBoolean data is required.
componentA component instance is required.
dateDate data is required.
guidA valid GUID value is required.
numericNumeric data is required.
queryQuery data is required. In case it isn't obvious, a query with no rows is still a valid query.
stringString data is required.
structStructure data is required.
uuidA ColdFusion UUID is required.
variableNameThe value must be a string and must be a valid ColdFusion variable name. It doesn't mean the variable has to exist, just that the variable name is valid.
voidSpeficies that no value is used. This only works in cffunction, not cfargument.
XMLXML data is required.

One last note - you can also specify a component name and brackets. For example, apple[]. This means that an array of apple components is required. Do note though that ColdFusion only checks the first item in the array.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Dustin posted on 8/30/2007 at 5:31 PM

"In the past, the only way to allow for any CFC would be to use 'any'."

This isn't entirely accurate. I've been doing this since MX 6.1, this is how I require a component of any type:

<cfargument name="bean" type="WEB-INF.cftags.component" required="yes" />

Comment 2 by Raymond Camden posted on 8/30/2007 at 5:32 PM

Ah - good one there Dustin!

Comment 3 by David posted on 8/30/2007 at 7:19 PM

Ray - could you give an example of why (and maybe how) I would do this?

Cheers,

Davo

Comment 4 by Raymond Camden posted on 8/30/2007 at 7:25 PM

There is a function up on CFLib that generates documentation for CFCs. That could be switched to use component.

I can't think of much more - my creativity today is low. ;)

Comment 5 by Dustin posted on 8/30/2007 at 9:00 PM

Personally, I use beans to pass data around. A bean is basically a component that stores data and has getters/setters for each data element in the bean. What I've done is created a validation object that I pass the bean to for validation. The reason I do this is because I can set up my validation rules inside a centralized validation XML file. This way, whenever my validation rules change, I don't have to search through all my code to find out where all it's being validated. I can simply change the validation rules for the mapping of the bean.

HTH

Comment 6 by Michael Long posted on 8/30/2007 at 10:32 PM

Not sure why this was there in the first place. "Any" was much too loose.

Comment 7 by Mike Rankin posted on 8/30/2007 at 11:39 PM

I'm pretty sure I've been using typed components as arguments and return values since cfmx6.1. I'll have to go back and check. The short hand for arrays and structures is definitely new, though.

I did a blog entry a few weeks back about the type checking feature/issue with sample code if you want to see it in action. http://mrmx.blogspot.com/20...

Depending on the application's setup, sometimes you have to use the full dot path to the component to properly type it.

Comment 8 by prashant posted on 11/16/2008 at 9:06 AM

I am using a "Query Factory" component for all queries for my big Application. I want to know is it good or bad to use this in place of CFquery as performance wise.

I am using same kind of process for Object Factory. so its good or bad performance wise?
Pls advice.

Here is the code for QueryFactory, this is rough code may need some corrections, goal is to find execting all queries with queryFactory is good or not? thanks always for suggestions.
--------------------------------------------------

<cfcomponent output="false" hint="query factory"
extends="exception.load">

<cfscript>
init();
</cfscript>

<cffunction name="init" access="public" output="false"
returntype="any" hint="Initialize the query factory object">
<cfargument name="DSN" required="false" type="any"
default="DSNOfApp" hint="DSN for query" />
<cfset var dsn = Arguments.DSN>

<cfscript>
if (trim(dsn) eq "")
{
dsn="defaultdsnforyourapplication";// default dsn for ur
application
}
setDSN(DSN);
setQueryString("");
</cfscript>
</cffunction>

<cffunction name="getType" access="public" returntype="any"
output="false" hint="Returns the objects type">
<cfreturn "utility.queryFactory" />
</cffunction>

<cffunction name="setDSN" access="private" output="false"
returntype="void" hint="Sets DSN for query">
<cfargument name="newDSN" type="string" required="true" />
<cfSet Variables.DSN = Arguments.newDSN />
</cffunction>

<cffunction name="getDSN" access="private" output="false"
returntype="any" hint="Gets DSN for query">
<cfscript>
if (isDefined('Variables.DSN')){
return Variables.DSN;
}else{
return '';
}
</cfscript>
</cffunction>

<cffunction name="setQueryString" access="public" output="false"
returntype="void" hint="Sets sql statements for query">
<cfargument name="newQueryString" type="string" required="true" />
<cfSet Variables.QueryString = Arguments.newQueryString />
</cffunction>

<cffunction name="getQueryString" access="public" output="false"
returntype="any" hint="Gets sql statements for query">
<cfscript>
if (isDefined('Variables.QueryString')){
return Variables.QueryString;
}else{
return '';
}
</cfscript>
</cffunction>

<cffunction name="getResultSet" access="public" output="false"
returntype="any" hint="Gets the resultset">
<cfscript>
if (isDefined('variables.resultSet')){
return variables.resultSet;
}else{
return '';
}
</cfscript>
</cffunction>

<cffunction name="setResultSet" access="private" output="false"
returntype="any" hint="Sets the resultset">
<cfargument name="newResultSet" type="any" required="true" />
<cfset variables.resultSet = Arguments.newResultSet>
</cffunction>

<cffunction name="execute" access="public" output="false"
hint="executes the query">
<cfset variables.qry="">

<cfquery name="qry" datasource="#getDSN()#">
#getQueryString()#
</cfquery>

<cfscript>
setResultSet(variables.qry);
</cfscript>

</cffunction>

</cfcomponent>

Comment 9 by Charles Robertson posted on 9/6/2016 at 8:25 PM

Do we need to specify the dot notation path to the component: type="com.services.apple" or will type="apple" suffice?

Comment 10 by Charles Robertson posted on 9/6/2016 at 8:33 PM

It's actually really important, if you are using an explicit setter that is injected with a component via a service factory, to specify the component name. The reason for this, is that setters are publicly accessible. This could lead to the wrong component being set from outside the CFC. Encapsulation should be maintained, wherever possible.

Comment 11 (In reply to #9) by Raymond Camden posted on 9/6/2016 at 8:42 PM

Going on my memory from 9 years ago, I think either works, but the "full" version is more specific and may be appropriate in a situation where you have framework code and your code intermingled.

Comment 12 (In reply to #11) by Charles Robertson posted on 9/6/2016 at 9:20 PM

Thanks Ray. I was thinking that just the name of the component might be more portable. The dot notation might require more updating, if components get moved into different folders etc. What do you think?

Comment 13 (In reply to #12) by Raymond Camden posted on 9/6/2016 at 9:21 PM

I'd say it depends. If I'm building something and it has a certain structure, than I think it is safe to assume that structure will exist, and if it doesn't, that's bad. :)

Comment 14 (In reply to #13) by Charles Robertson posted on 9/6/2016 at 9:26 PM

I see what you mean, so your idea is that dot notation enforces more specificity, and therefore provides more security? Hmmm...decisions, decisions, decisions...

Comment 15 (In reply to #14) by Raymond Camden posted on 9/6/2016 at 9:40 PM

Yes... but is it necessary? I don't think you can say so 100%.

(And I wouldn't say 'more security', just more reliability.

Comment 16 (In reply to #13) by Charles Robertson posted on 9/6/2016 at 9:40 PM

Also, is the dot notation relative to the web root? Like:

/com/services/apple.cfc

/com/services/pear.cfc

pear.cfc

<cffunction name="setApple">
<argument name="fruit" type="com.services.apple"/>
<cfset variables.apple="arguments.fruit">
</cffunction>

Comment 17 (In reply to #16) by Raymond Camden posted on 9/6/2016 at 9:42 PM

It follows the same rules as when CF makes a new component via createObject("component", "X")

My understanding is that it looks for mappings that match the name, so

blog.user

would make CF look for a cf mapping called blog.

*Then* it should look for a relative directory from the code calling it... or the code creating it.

Not 100% sure. Sorry. My CF is rusty.

Comment 18 (In reply to #15) by Charles Robertson posted on 9/6/2016 at 9:44 PM

I don't understand why it is more reliable. Surely, just using the name, would work 100% of the time, because the argument matches the name metadata of the component object passed in, with the name provided in its type attribute?

What I mean about security, is that the component passed in, would have to reside in a specific directory, so less chance of spoofing.

Comment 19 (In reply to #18) by Raymond Camden posted on 9/6/2016 at 9:46 PM

As I said, my specific example was for framework code, or code you include in other projects. So imagine I've got an open source blog you drop into your project and it looks for a component "user". Is it user for the blog, or your site as a whole? That's what I mean about how specificity could benefit you.

Yes - it is a bit of an edge case, but it's the first thing I thought of.

Comment 20 (In reply to #19) by Charles Robertson posted on 9/6/2016 at 9:51 PM

I guess it depends how the type attribute resolves itself? I presumed it would use the component object metadata passed in. So 'name' would match 100% of the time, but 'dot notation' would only match if the metadata component path matches as well?

Comment 21 (In reply to #20) by Raymond Camden posted on 9/6/2016 at 9:54 PM

Yeah this is where I have to bow out. :)

For years I can remember wanting the ability to supply a relative path, ie

../utils/etc

to ensure a 100% precise connection.

Shrug. Good luck. :)

Comment 22 (In reply to #21) by Charles Robertson posted on 9/6/2016 at 9:58 PM

Fair enough, Ray. I will play around with this tomorrow. The truth is out there...

Comment 23 by Roberto Fonseca posted on 11/3/2016 at 4:06 PM

Hi!
I'm back to coldfusion from Java. But how could I Map an Typed Array, such as a List of specific Object, like User.cfc List<user.cfc>() ? What's the workarround for?

Comment 24 (In reply to #23) by Raymond Camden posted on 11/3/2016 at 6:44 PM

As far as I know, you can't.

Comment 25 (In reply to #24) by Roberto Fonseca posted on 11/3/2016 at 6:58 PM

After a while, refining Google Searches I found this:

http://help.adobe.com/en_US...

:)

Comment 26 (In reply to #25) by Raymond Camden posted on 11/3/2016 at 7:03 PM

How does that relate to using an array of a particular type? As far as I can see, that's for CF's ORM integration.

Comment 27 (In reply to #26) by Roberto Fonseca posted on 11/3/2016 at 7:07 PM

Actually you can use the same way

component name="Checklist" implements="Adapter" {

property name="id" type="String"; // ID da Avaliação
property name="itens" type="Array" fieldtype="one-to-many" cfc="ChecklistItem";

So your component will use a Typed Array.

Comment 28 (In reply to #27) by Raymond Camden posted on 11/3/2016 at 7:14 PM

Ah, ok.