Duncan's blog

March 4, 2009

Introduction to Coldfusion Components (CFCs)

Here’s something I wrote up for work, a very basic beginner’s guide to CFCs. Nothing too technical, but I thought it might be of use for some people. I’d appreciate any feedback or questions.

What is a CFC?

At its simplest, a collection of functions, with a filename that ends in .cfc. The basic structure is like:

<cfcomponent>
      <cffunction>
            ...
      </cffunction>
</cfcomponent> 

A more complete example:

<cfcomponent hint="Library of mathematical functions">
      <cffunction
            name="getAtoPowerOfB"
            output="No"
            returntype="numeric"
            description="Returns value of A^n"
            hint="Returns A to the power of n"> 

            <cfargument
                  name="A"
                  required="Yes"
                  type="numeric"
                  hint="Base">
            <cfargument
                  name="n"
                  required="Yes"
                  type="numeric"
                  hint="Exponent"> 

            <cfset var power = arguments.A^arguments.n> 

            <cfreturn power>
      </cffunction>
</cfcomponent> 

Where to store CFCs

CFC files can be stored:

  • In the same folder as the current .cfm page
  • In the components folder as defined in the CF Administrator

The preferred method is in the components folder, so it becomes accessible to other pages.

Initialising a CFC

The Init() function isn’t mandatory, but is a de facto standard.

<cfcomponent>
      <cfset this.datasource = ""> 

      <cffunction name="Init">
            <cfargument  
                  required="Yes"
                  name="DataSource"
                  type="string"> 

            <cfset this.datasource = arguments.DataSource>
      </cffunction>
</cfcomponent>

Instantiating and calling a CFC

Method 1:

Pass individual named arguments in the cfinvoke tag.

<cfinvoke
      component = "foo"
      method = "getFoo"
      returnVariable = "blah"
      x="4"
      y="2"
      name="Duncan">

Method 2:

Pass arguments as a structure in the cfinvoke tag.

<cfset args = StructNew()>
<cfset args.x = 4>
<cfset args.y = 2>
<cfset args.name = "Duncan"> 

<cfinvoke
      component = "foo"
      method = "getFoo"
      returnVariable = "blah"
      argumentCollection = "#args#">

Method 3:

Pass individual arguments with cfinvokeargument tags.

<cfinvoke
      component = "foo"
      method = "getFoo"
      returnVariable = "blah"> 
            <cfinvokeargument name="x" value="4">
            <cfinvokeargument name="y" value="2">
            <cfinvokeargument name="name" value="Duncan"> 
</cfinvoke>

Method 4:

Create an instance of the CFC, then use that to call functions. This is preferable over any of the cfinvoke methods.

<cfset ComponentFoo = CreateObject("component", "path.to.components.foo")>  

There are two ways to then call the functions.

  1. Don’t specify argument names. Arguments have to be in the same order as specified in the function.
    <cfset blah = ComponentFoo.getFoo(4, 2, "Duncan")> 
    
  2. Specify all argument names. Order of the arguments doesn’t matter. This way is also preferable because it makes it clearer in your code what the arguments are, without having to refer back to the CFC.
    <cfset blah = ComponentFoo.getFoo(x=4, y=2, name="Duncan")>
    

Scopes

CFCs have three scopes:

  • this
  • variables
  • var

The this scope is for variables that need to be accessible to the calling .cfm or .cfc.

The variables scope is for variables that are private to just this cfc. Any function within the CFC can access these.

The var scope is for variables that are private to just one function. It is essential that all local function variables are properly var scoped.

The var scope is implicit; you don’t prefix variables with the scope name, like you do most other scopes. i.e. you can’t say <cfset x = var.y>

For instance:

<cfcomponent>
      <cfset this.directory = "C:Inetpubwwwroot">
      <cfset variables.datasource = "DSN"> 

      <cffunction name="doStuff">
            <cfset var name = ""> <!--- Good --->
            <cfset age = 0>      <!--- Bad! --->
      </cffunction> 
</cfcomponent> 

<cfset ComponentFoo = CreateObject("component", "path.to.components.foo")>  
<cfset blah = ComponentFoo.directory> <!--- This will work --->
<cfset blah = ComponentFoo.datasource> <!--- This will throw an error  --->

Best practice

  • Var scoping is essential; use the varscoper tool to check your CFCs
  • Use Output=”false” on all cfcomponent and cffunction tags if possible
  • Don’t put cfoutput in a function, return information to the calling page instead
  • Use the hint attribute on <cfcomponent>, <cffunction> and <cfargument> for documentation
  • Function names should be verbs, e.g. getData, updateName, changePassword
  • Ideally, functions should be loosely-coupled, i.e. functions shouldn’t refer to shared scopes (session, application, request etc), or to variables that exist outwith the CFC. So don’t refer to request.datasource, instead when you initialise the CFC, set the datasource in the this or variables scope (passed in as an argument to the initialisation function) and refer to that instead.

Create a free website or blog at WordPress.com.