Introducing ColdFusion Components

Exclusive offer: get 50% off this eBook here
Object-Oriented Programming in ColdFusion

Object-Oriented Programming in ColdFusion — Save 50%

Break free from procedural programming and learn how to optimize your applications and enhance your skills using objects and design patterns

£14.99    £7.50
by Matt Gifford | October 2010 | Web Development

ColdFusion Components, commonly known as CFCs, were introduced in ColdFusion MX. In essence, they are simple templates written in existing CFML tags and CFScript. As such, they are not complex, confusing, or difficult to understand. If you can code CFML, you can create CFCs.

In this article by Matt Gifford, author of Object-Oriented Programming in ColdFusion, we will cover the following:

  • The basic structure of a ColdFusion component
  • The component tags, functions, and methods
  • Passing parameters using the argument scope

 

Object-Oriented Programming in ColdFusion

Object-Oriented Programming in ColdFusion

Break free from procedural programming and learn how to optimize your applications and enhance your skills using objects and design patterns

  • Fast-paced easy-to-follow guide introducing object-oriented programming for ColdFusion developers
  • Enhance your applications by building structured applications utilizing basic design patterns and object-oriented principles
  • Streamline your code base with reusable, modular objects
  • Packed with example code and useful snippets
        Read more about this book      

(For more resources on ColdFusion, see here.)

For those with any experience with ColdFusion, components should be relatively commonplace. Object-Oriented Programming (OOP) relies heavily on the use of ColdFusion components, so before proceeding onto the ins and outs of OOP, let's re-familiarize ourselves with components within ColdFusion.

ColdFusion Components use the same ColdFusion Markup Language (CFML) as 'standard' ColdFusion pages. The core difference is the file extension—components must be saved with a .cfc file extension as opposed to the .cfm file extensions for template pages.

The basic structure of a ColdFusion Component is:

  • The component (the page within which you create the code to hold data or perform functions)
  • The methods available to run within the CFC, also known as functions

In simple terms, CFCs themselves form a framework within ColdFusion, allowing you to write structured, clear, and organized code. They make application development easier to manage, control, and maintain.

ColdFusion Components use the same CFML as 'standard' ColdFusion pages. The core difference is the file extension.

Why use CFCs?

It is not unusual for applications to grow and seem overly complex. Pages containing detailed information, such as business logic, data access and manipulation, data validation, and layout/presentation logic, can become untidy and hard to manage.

Creating and developing applications using CFCs enables you to separate the code logic from the design and presentation, and build an application based around, if not using, traditional Model View Controller (MVC) framework methodologies.

Utilizing CFCs and creating a clear structured format for your code will help reduce the complexity of logic within your pages and improve the application speed. Having a clearly structured, well organized code base will make it easier to develop as an individual and share resources within a team. This is the instant benefit of CFC development.

A well-written CFC will allow you to reuse your functions, or methods, across your entire application, helping to reduce the risk of code duplication. It will keep your component libraries and code base to a more easily manageable size, preventing it from becoming convoluted and difficult to follow.

ColdFusion components are an incredibly powerful and valuable means of creating efficient code. They allow you to:

  • Share properties and variables between other methods and functions
  • Share and interact with functions contained within other CFCs
  • Inherit the properties and methods of a base component
  • Overwrite methods and functions within other components

CFCs also give you the ability to clearly document and comment your code, letting you and other developers know what each function and property should do, what it should be expecting to receive to do the job and what output it will give you. ColdFusion components are able to read themselves and display this data to you, using a form of introspection.

Although CFCs are an effective tool for code reuse, this is not to say they should be used for every reusable function within your application. They are not a complete replacement for custom tags and user-defined functions.

When you load a CFC (instantiate the component), this uses up more processing time than it would to call a custom tag or a User-Defined Function (UDF) into use. Once a CFC has been instantiated, however, calling a method or function within the component will take approximately the same time as it would to call a UDF.

It is important, therefore, that CFCs should not necessarily be used as a complete replacement for any UDFs or custom tags that you have in your application. Any code you write can, of course, be optimized, and changes can be made as you learn new things, but UDFs and custom tags perform perfectly well. Using them as they are will help to keep any processing overheads on your application to a minimum.

Grouping your functions

You may have already written custom tags and user-defined functions that allow similar functionality and reusability, for example, a series of UDFs that interact with a shopping cart. By grouping your functions within specific components according to their use and purpose, you can successfully keep your code library organized and more efficient.

You can also further clean your code library by compiling or grouping multiple related components into a package, clearly named and stored in a directory within your application.

Organizing your components

A typical method for organizing your CFC library is to create a directory structure based on your company or domain name, followed by a directory whose name references the purpose of the included components, for example, 'com.coldfumonkeh.projecttracker' in the webroot of your application.

Within this directory, you would then create a directory for each group (or package), of components, with a name reflecting or matching the component name and purpose.

Object-Oriented Programming in ColdFusion

Use your ColdFusion Components to create a component structure, or a library, that contains grouped methods and functions, particularly if the methods share properties or data.

The ColdFusion component tags

You can use these following tags to create a ColdFusion Component.

Tag Purpose
cfcomponent The core CFC tag that defines the component structure. All other content in the component is wrapped within this tag.
cffunction Creates a method (function) within the component.
cfargument Creates a parameter, otherwise known as an argument, to be sent to the function.
cfproperty Can be used to define and document the properties within your component. Can also be used to define variables within a CFC that is used as a web service.

These previously mentioned tags are written within the .cfc file that defines the ColdFusion component.

In the world of object-oriented programming, you will commonly hear or see reference to the word 'Class'. A class is essentially a blueprint that is used to instantiate an object, and typically contains methods and instance variables.
When discussing a Class in the context of ColdFusion development, we are basically referencing a ColdFusion component, so when you see or read about classes, remember it is essentially an alias for a CFC.

Our first component

To get started, in this example, we will create a component and functions to output the message "Hello world".

Create a new file called greetings.cfc and save it within your ColdFusion webroot.

The following is a component base tag; add this code into the new CFC to define the component:

<cfcomponent displayName="greetings">
</cfcomponent>

Listing 1.1 – component base tags

As you can see, the name attribute within the CFC matches the name of the file. The cfcomponent tags form the base structure of our ColdFusion Component. No other code can be placed outside of these tags, as it will simply display an error.

It may be helpful to think of the cfcomponent tag as the wrapping paper on a parcel. It forms the outer shell of the package, holding everything else nicely in place.

Defining a method

We have now created the component, but at the moment it does not actually do anything. It has no function to run. We need to add a method into the CFC to create a function to call and use within our application. The following code is a basic function definition; place it between the opening and closing cfcomponent tags:

<cffunction name="sayHello">
<!--- the CFML code for the method will go here --->
</cffunction>

Listing 1.2 – basic function definition

You have now added a method to the CFC. The cffunction tags are nested within the cfcomponent tags. We now need to add some CFML code within the cffunction tags to create our method and perform the operation. Let's create a variable within the function that will be our display message. The following code is for declaring a string variable; place it inside the cffunction tags:

<cffunction name="sayHello">
<cfset var strHelloMessage = 'Hello World!' />
</cffunction>

Listing 1.3 – declaring a string variable

We have created a string variable containing the text to display to the browser.

Returning the data

To return the data we need to add an extra tag into the method. This is possible by using the cfreturn tag, which returns results from a component method. The cfreturn tag has one required attribute that is the expression or value you wish to return.

Add the following code to your CFC so our method will return the welcome message and the completed component will look like this:

<cfcomponent displayName="greetings">
<cffunction name="sayHello">
<cfset var strHelloMessage = 'Hello World!' />
<cfreturn strHelloMessage />
</cffunction>
</cfcomponent>

Listing 1.4 – returning data from the function

ColdFusion 9 scripted components

Since the release of ColdFusion 9, developers now have the ability to also write ColdFusion components in complete script syntax instead of pure tag form.

To write the previous component in this format, the code would look as follows:

component
displayname="greetings"
{
function sayHello(){
// the CFML code for the method will go here
var strHelloMessage='Hello World';
return strHelloMessage;
}
}

Listing 1.5 – component declaration in the script syntax

Although written using cfscript syntax, there is no requirement to wrap the code within <cfscript> tags, instead we can write it directly within the .cfc page.

We do not even need to contain the code within cfcomponent tags, as the entire content of the component will be compiled as cfscript if left as plain text without tags.

Creating your object

There it is, a simple ColdFusion Component. The method is created using the cffunction tags, wrapped up nicely within the cfcomponent tags, and the value returned using the cfreturn tag. Now that we have written the function, how do we call it?

In this example, we will call the component and run the method by using the createObject() function. Create a new file called hello.cfm and add the following code to the template:

<cfset objGreeting = createObject('component', 'greetings') />
<cfoutput>#objGreeting.sayHello()#</cfoutput>

Listing 1.6 – creating the component object

In the previous code, we have created an instance of the greetings CFC, which we can reference by using the objGreeting variable. We have then accessed the sayHello() method within the component, surrounded by cfoutput tags, to display the returned data.

Save the file and view it within your browser. You should now see the welcome message that we created within the method.

Object-Oriented Programming in ColdFusion

Restricting your functions to scopes

Imagine we are sending some data through to a login page in our application within the URL scope; the first and last name of a particular person. On the page, we want to join the two values and combine them into one string to form the individual's full name. We could write the code directly on the page, as follows:

<cfoutput>
Hello, #URL.firstName# #URL.lastName#
</cfoutput>

Listing 1.7 – displaying URL variables as a string

Although this works, you can revise the code and transform it into a ColdFusion function to concatenate the two values into the required single string and return that value:

<cffunction name="getName">
<cfset var strFullName = URL.firstName & ' ' & URL.lastName />
<cfreturn strFullName />
</cffunction>

Listing 1.8 – concatenate variables into string

You can then call this function within your .cfm page to output the resulting string from the function:

<cfoutput>
#getName()#
</cfoutput>

However, within this code you have restricted yourself to using only the specific URL scope. What if the first name and last name values were in the FORM scope, or pulled from a query? This block of code is useful only for values within the form scope.

Using arguments within your methods

To allow us to be able to pass in any parameters into the getName() function, we need to use the cfargument tag to send data into the method. By changing the function in the following code example, the method will create the concatenated string and produce the same results from two parameters or arguments that you choose to pass in.

<cffunction name="getName">
<cfargument name="firstName" type="string" />
<cfargument name="lastName" type="string" />
<cfset var strFullName = arguments.firstName & '
' & arguments.lastName />
<cfreturn strFullName />
</cffunction>

Listing 1.10 – using arguments within your function

The cfargument tag creates a parameter definition within the component method, and allows you to send in arguments for inclusion into the functions.

The Arguments scope

The Arguments scope only exists in a method. The scope contains any variables that you have passed into that method, and you can access the variables within the Arguments scope in the following ways:

  • using structure notation - Arguments.variablename or Arguments["variablename"]
  • using array notation - Arguments[1]

The Arguments scope does not persist between calls to available CFC methods, meaning that you cannot access a value within the Arguments scope in one function from inside a different function.

Redefine the function parameters

By defining two arguments and sending in the values for the first and last names, you have created an unrestricted function that is not tied to a specific scope or set of hardcoded values. You can instead choose what values to pass into it on your calling page:

<cfoutput>
#getName('Gary', 'Brown')#
</cfoutput>

Listing 1.11a – sending parameters into our function

Now that we have removed any restrictions to the values we pass in, and taken away any references to hardcoded variables, we can reuse this function, sending in whichever values or variables we choose to. For example, we could use variables from the FORM scope, URL scope, or query items to concatenate the string:

<cfoutput>
#getName(form.firstName, form.lastName)#
</cfoutput>

Listing 1.11b – sending parameters into our function

Let's take our getName() method and add it into the greeting.cfc file. By doing so, we are grouping two methods that have a similarity in purpose into one component. This is good programming practice and will aid in creating manageable and clearly organized code.

Our greeting.cfc should now look like this:

<cfcomponent name="greetings">
<cffunction name="sayHello">
<cfset var strHelloMessage = 'Hello World!' />
<cfreturn strHelloMessage />
</cffunction>
<cffunction name="getName">
<cfargument name="firstName" type="string" />
<cfargument name="lastName" type="string" />
<cfset var strFullName = arguments.firstName & '
' & arguments.lastName />
<cfreturn strFullName />
</cffunction>
</cfcomponent>

Listing 1.12 – revised greeting.cfc

Combining your methods

As we have seen, you can easily access the methods within a defined CFC and output the data in a .cfm template page.

You can also easily access the functionality of one method in a CFC from another method. This is particularly useful when your component definition contains grouped functions that may have a relationship based upon their common purpose.

To show this, let's create a new method that will use the results from both of our existing functions within the greetings.cfc file. Instead of displaying a generic "Hello World" message, we will incorporate the returned data from the getName() method and display a personalized greeting.

Create a new method within the CFC, called personalGreeting.

<cffunction name="personalGreeting">
<cfargument name="firstName" type="string" />
<cfargument name="lastName" type="string" />
<cfscript>
strHello = sayHello();
strFullName = getName(firstName=arguments.firstName,
lastName=arguments.lastName);
strHelloMessage = strHello & ' My name is ' & strFullName;
</cfscript>
<cfreturn strHelloMessage />
</cffunction>

Listing 1.13 – personalGreeting method

Within this method, we are calling our two previously defined methods. The returned value from the sayHello() method is being stored as a string variable, "strHello".

We then retrieve the returned value from the getName() method and store this in a string variable "strFullName". As we have written the getName() function to accept two arguments to form the concatenated name string, we also need to add the same two arguments to the personalGreeting() method , as done in the previous code. They will then be passed through to the getName() method in exactly the same way as if we were calling that function directly.

Using the two variables that now hold the returned data, we create our strHelloMessage variable, which joins the two values, and is then returned from the method using the cfreturn tag.

In this method, we used CFScript instead of CFML and cfset tags, which were used in our previous functions. There is no hard and fast rule for this. You can use whichever coding method you find the most comfortable.

Let's call this method on our hello.cfm template page, using the following code:

<!--- instatiate the component --->
<cfset objGreeting = createObject('component', 'greetings') />
<!--- access the method and assign results to a string --->
<cfset strPersonalGreeting = objGreeting.personalGreeting(
firstName="Gary", lastName="Brown") />
<cfoutput>#strPersonalGreeting#</cfoutput>

Listing 1.14 – calling the personalGreeting method

We are sending in the same arguments that we were passing through to the original getName() method, in the same way. This time we are passing these through using the newly created personalGreeting() method.

You should now see a personalized greeting message displayed in your browser:

Object-Oriented Programming in ColdFusion

Object-Oriented Programming in ColdFusion Break free from procedural programming and learn how to optimize your applications and enhance your skills using objects and design patterns
Published: October 2010
eBook Price: £14.99
Book Price: £24.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on ColdFusion, see here.)

Protecting your local variables

In our previous personalGreeting() method, we included two separate functions, sayHello() and getName(), into the main method. This is not an uncommon practice, and is what you would expect when writing detailed components with relationships between its included functions.

One issue that can arise when developing in this way is when two or more methods contain a variable of the same name and the value of that variable is accessed or changed by one of the methods.

As an example, the following code contains two functions, baseNumber() and multiplyNumbers().

While the cfcomponent tag has been excluded in this example, this could also easily be turned into a CFC by wrapping the functions within cfcomponent tags.

<cfoutput>
<cffunction name="baseNumber" returnType="numeric">
<cfargument name="a" type="numeric" required="true" />
<cfset x = arguments.a />
<cfreturn x />
</cffunction>
<cffunction name="multiplyNumbers" returntype="string">
<cfargument name="a" type="numeric" required="true" />
<cfargument name="b" type="numeric" required="true" />

<!--- multiply our basenumber value by 10 --->
<cfset x = 10 />
<cfset y = baseNumber(a) />
<cfreturn y & " multiplied by " & x & " = " & x * arguments.b />
</cffunction>
<cfloop from="1" to="10" index="i">
#multiplyNumbers(i,i)#<br />
</cfloop>
</cfoutput>

Listing 1.15 – two user-defined functions

A cfloop tag runs a loop from 1 to 10. The multiplyNumbers() function accepts two arguments. In this example, these are both the index numbers of the loop. We want to multiply our baseNumber value (argument 'a'), by 10 for each loop, creating a 10 times table list. To do this, the multiplyNumbers() function has a hardcoded value (x) that is set to the value of 10.

The desired results you would expect from this code should be:

1 multiplied by 10 = 10

2 multiplied by 10 = 20

However, this is not the case. If you save the code to a .cfm template and run it in your browser, you will get the following result:

Object-Oriented Programming in ColdFusion

This is clearly not the result you would expect. So what's happening to cause this issue? Let's take another look at our two functions:

<cffunction name="baseNumber" returnType="numeric">
<cfargument name="a" type="numeric" required="true" />
<cfset x = arguments.a />
<cfreturn x />
</cffunction>

<cffunction name="multiplyNumbers" returntype="string">
<cfargument name="a" type="numeric" required="true" />
<cfargument name="b" type="numeric" required="true" />
<!--- multiply our basenumber value by 10 --->
<cfset x = 10 />
<cfset y = baseNumber(a) />
<cfreturn y & " multiplied by " & x & " = " & x * arguments.b />
</cffunction>

Listing 1.16 – examining the two methods

You can see that both functions have a variable called x. The baseNumber() function stores the value of the argument as the x variable, which it returns into the multiplyNumbers() function for use in the equation. The multiplyNumbers() function also has a variable called x, which is the hardcoded number we wish to use as a multiplier, in this case 10.

Within the function, the returned value from the baseNumber() method is assigned to y for use in the equation, but as this included function is run, it overwrites the value of the hardcoded x variable with its own x value. This, in turn, is passed into the equation, which throws off the expected results.

In the previous example, the x value in both functions is public, meaning that it can be altered or overwritten by any included functions, or if in a CFC, any defined method within the component. They are, in essence, set as 'open' variables that can be accessed and amended.

By running the two functions in this way, with openly accessible variables, it has the effect of ruining our ten times table. Imagine that we had a method controlling the shopping cart in an e-commerce application, updating quantities and costs, perhaps even stock levels of products. If we left these public variables open, they could be accessed by any included functions, and the values could change dramatically altering our shopping cart and its data.

Using the Var scope

To avoid this issue, the best practice is to set any local function variables to only be accessed by that particular function. This is achieved by using the Var keyword when setting variables. By applying variable to the Var scope, you are restricting public access to them and declaring that they are only accessible within the method in which they are defined. This removes any chance that external functions will corrupt the values.

You should always use the Var keyword on variables that are used only inside of the function in which they are declared.

Let's alter our code to include the Var keyword to ensure the variables are available only to the functions in which they are written:

<cffunction name="baseNumber" returnType="numeric">
<cfargument name="a" type="numeric" required="true" />
<cfset Var x = arguments.a />
<cfreturn x />
</cffunction>

<cffunction name="multiplyNumbers" returntype="string">
<cfargument name="a" type="numeric" required="true" />
<cfargument name="b" type="numeric" required="true" />
<cfset Var x = 10 />
<cfset var y = baseNumber(a) />
<cfreturn y & " multiplied by " & x & " = " & x * arguments.b />
</cffunction>

Listing 1.17 – Var scoping our variables

If we save the code with the Var keyword applied to the variables and view the page in the browser, you will now see the correct results displayed:

Object-Oriented Programming in ColdFusion

Regardless of the type of variable you are using within your component methods, (a query, string, integer, array, or structure) if it is used only within the function in which it is declared, it needs to be Var scoped to protect it and to avoid any unwanted amendments by other functions.

Placing your Var scoped variables

Up to ColdFusion 8, all Var scoped variables were required to be placed after any arguments within the function (if there are any included), and before any CFML.

Enhancements in ColdFusion 9 removed this restriction, and Var scoped variables can be placed anywhere within a code block or function.

Naming your Var scoped variables

While there are no strict conventions when naming your Var scoped variables, be aware that a naming conflict will arise if your local variable name is the same as any defined argument name (or the name of another local variable).

<cffunction name="baseNumber" returnType="numeric">
<cfargument name="x" type="numeric" required="true" />
<cfset Var x = arguments.x />
<cfreturn x />
</cffunction>

Listing 1.18 – baseNumber function

For example, if we have written the baseNumber() method as in the previous code, with the argument and local variable both called x, this would display an error, as a local variable within a function cannot be declared twice.

Accessing your CFC

Once you are ready to use your CFCs, you need to access the methods placed within. There are two ways to access a CFC:

  • object instantiation
  • invoking the CFC

Instantiating the object

When you instantiate a CFC, you create an instance of the component. This instance preserves the data within the CFC for as long as it exists. You would typically create an instance of a component at the top of the page. By doing so, you would have access to its methods and functions on the entire page, without having to create a new instance for each call.

There are three methods available to you to create an instance of the CFC:

  • createObject
  • cfobject
  • using the NEW operator in ColdFusion 9

Using the createObject function

As used in our earlier examples, the createObject() function creates and returns a ColdFusion object.

<cfscript>
objGreeting = createObject('component', 'greetings');
</cfscript>

Listing 1.19

Here, we are creating a new instance of the "greetings" component. The first parameter tells the createObject() function that we want a component, and the second references the name of the component of which we want to create an instance.

While the CFC is in the same directory as the calling page, in the previous example, if we had the CFC within a different directory in the webroot, for example, a folder named components; this second parameter would read as follows:

createObject('component', 'components.greetings');

This is because the second parameter is a dot notation representation of the path to the component.

Using the cfobject tag

Similar to the createObject() function, the cfobject tag has three attributes.

<cfobject name="greetingsObject" component="greetings"
type="component" />

Listing 1.20

The name attribute defines the name of the returned variable of the CFC instance so you can access the component to use your methods. The component attribute represents a dot notation path to the CFC you wish to instantiate. The third attribute, type, is optional, and has the default value component.

Using the NEW operator

The enhancements in ColdFusion 9 now provide an alternative way of creating an instance of a component object without using the createObject() function.

We can now create the object through the use of the new operator, like so:

<cfscript>
// create the object
objGreeting = new greeting();
</cfscript>

Listing 1.21

Using cfinvoke

You can invoke your component and access the method simultaneously by using the cfinvoke tag. When you invoke (call) the CFC using this tag, you are not creating an instance of the component that will be preserved and available for use elsewhere within your CFML page. Instead, you are creating an instance of the CFC that comes into existence as soon as you invoke the method, and ceases to exist as soon as the requested method has returned a result.

In essence, you are bringing the component to life long enough to get the details you need from it and closing it down as soon as the information is returned.

The cfinvoke tag

Let's make a call to our sayHello() method within the greetings.cfc component.

Add the following code to your hello.cfm template page:

<cfinvoke component="greetings" method="sayHello"
returnVariable="strHello" />
<cfoutput>#strHello#</cfoutput>

Listing 1.22

Here, we are invoking the greetings component, selecting the method within the CFC that we want to access, (in this case the sayHello() function) and assigning a variable (strHello), to which the returned data will be saved for us to access it within the page.

Outputting the returnVariable onto the page will provide us with the same result as we have seen before.

Using cfinvokeargument

The cfinvoke tag also allows us to pass in parameters to the methods we are calling, by means of the cfinvokeargument tag.

<cfinvoke component="greetings" method="personalGreeting"
returnVariable="strPersonalGreeting">
<cfinvokeargument name="firstName" value="Matt" />
<cfinvokeargument name="lastName" value="James" />
</cfinvoke>

Listing 1.23

We are sending our parameters used in the personalGreeting() method in a similar format to the cfargument tag, using the cfinvokeargument tag, which is nested within the cfinvoke tag. The cfinvokeargument tag takes the name and value of the argument and sends it into the method you are calling.

Using attributes as arguments

Alternatively, when using the cfinvoke tag, you can send through the parameters as named attribute-value pairs, providing one attribute per argument.

<cfinvoke component="greetings" method="personalGreeting"
firstName="Gary" lastName="Brown"
returnVariable="strPersonalGreeting" />

Listing 1.24

You can see in the previous code that the firstName and lastName parameters are written as attributes within the cfinvoke tag itself.

Using an argument collection

The optional argumentCollection attribute for the cfinvoke tag accepts a structure in the form of an associative array of arguments to pass into the method.

<cfscript>
// create a structure to hold the values
stuArguments = structNew();
stuArguments.firstName = "James";
stuArguments.lastName = "Brown";
</cfscript>
<cfinvoke component="greetings" method="personalGreeting"
argumentCollection="#stuArguments#"
returnVariable="strPersonalGreeting" />

Listing 1.25

The structure names must match the names of the arguments within the method.

Passing arguments into an instance method call

As you have seen in the previous examples, we have sent parameters into the methods on our pages. There are two options available to send your arguments into your method call on an instantiated object.

As a list

You can send the two arguments through as a comma-delimited list. If you use this option, the order of the parameters you pass into the function call must match the order of arguments defined within the method.

In our personalGreeting() function the first name is the first argument, and the last name is the second argument, therefore you would do the following to call the method:

<cfset strPersonalGreeting =
objGreeting.personalGreeting("Daft","Vader") />

Listing 1.26

As named values

An alternative method of sending arguments into a function is to use named values. This option ensures that the values passed through are assigned to the correct argument within the method. This means you do not have to place the parameters within the function call in any specific order, as the name of the parameter will match the name of the argument within the method.

<cfset strPersonalGreeting =
objGreeting.personalGreeting(firstName="Daft",lastName="Vader") />

Listing 1.27

As an argumentCollection

As mentioned earlier, we also have the ability to send arguments through to the method using the argumentCollection attribute, and send through a structure of values.

Arguments in action

Let's look at another simple use case for creating reusable components and functions, which will also highlight some benefits of passing arguments into your methods.

Merging your functions into one

Create a new CFC called contacts.cfc, and add your cfcomponent tags to define the component.

Add the following method in the contacts.cfc file. This new function runs a SELECT query on the Project Tracker application database to retrieve a recordset of all contacts:

<cffunction name="getContacts">
<cfset var rstContacts = "" />
<cfquery name="rstContacts" datasource="projectTracker">
SELECT firstName,lastName FROM Owners
</cfquery>
<cfreturn rstContacts />
</cffunction>

Listing 1.28

We now have a function returning our query data. We also want to have a query to pull out a specific record based on the record ID of a particular person.

We can create a second function to handle this as well:

<cffunction name="getContact">
<cfargument name="ID" type="numeric" />
<cfset var rstContact = "" />
<cfquery name="rstContact" datasource="projectTracker">
SELECT firstName,lastName FROM Owners
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer"
value="#arguments.ID#" />
</cfquery>
<cfreturn rstContact />
</cffunction>

Listing 1.29

These two methods within the contacts.cfc file interact with our database and return query data. However, the two queries pull out exactly the same information from the database. The only difference between the queries is that getContact() returns records for a specific user, based upon the ID value. We can easily streamline our CFC by combining these two methods into one, which will remove unnecessary code from our files and theoretically turn one function into two.

Using cfargument to combine your methods

In this example, we will combine the two SELECT queries into one method, using the cfargument tag.

<cffunction name="getContact">
<cfargument name="ID" type="numeric" default="0" />
<cfset var rstContact = "" />
<cfquery name="rstContact" datasource="projectTracker">
SELECT firstName,lastName FROM Owners
<cfif arguments.ID GT 0>
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer"
value="#arguments.ID#" />
</cfif>
</cfquery>
<cfreturn rstContact />
</cffunction>

Listing 1.30

The cfargument tag has a default attribute, which allows you to provide a default value for an argument if you do not pass one into the method. As the customer ID parameter type is set to "numeric", we have set the default value of the argument to "0". This value will now always be available within our method and will stay at the default value until a parameter is passed in to the function.

By providing a default value, we are now able to wrap a cfif tag block around the WHERE clause of the query. If the value of arguments.ID (the value of the parameter within the arguments scope) is greater than 0, that is, if we have passed a numeric value into the method ourselves, then include the WHERE clause when running the SQL within the cfquery tags.

By doing this, we have merged the two methods within the contacts.cfc into one, optimizing our code and allowing it to perform more than one function.

Let's run this query. Create a new page template called query.cfm, and paste in the following code to create the object and run the method without sending in any parameters:

<!--- instantiate the object --->
<cfset objContacts = createObject('component', 'contacts') />
<!--- dump the results --->
<cfdump var="#objContacts.getContact()#" />

Listing 1.31

If no ID value is sent through as a parameter, meaning the default value is 0, the method will return the full recordset of all content from the Owners database table:

Object-Oriented Programming in ColdFusion

Amend the code by adding in a numeric value to pass through as the ID argument:

<!--- dump the results --->
<cfdump var="#objContacts.getContact(2)#" />

Listing 1.32

If we provide an ID within the argument, the method will only return the row for the contact that has the matching ID value:

Object-Oriented Programming in ColdFusion

The dumped object now shows us the parameter sent through in the arguments scope and the SQL query that now includes the WHERE clause.

The simple solution of using an argument with a default value and a cfif statement to control the flow has streamlined and reduced the amount of extraneous code within your application.

Object-Oriented Programming in ColdFusion Break free from procedural programming and learn how to optimize your applications and enhance your skills using objects and design patterns
Published: October 2010
eBook Price: £14.99
Book Price: £24.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on ColdFusion, see here.)

Creating an object constructor

In our contacts.cfc, we have defined a method which contains a query. This query has the datasource name attribute defined to correctly reference the database setup within the ColdFusion administration console.

As with all good development, we want to restrict hardcoding any values or references wherever we can and instead use variables to define them, (in this case the datasource name attribute).

A common practice in application development is to create your datasource name and store it in the Application scope, ensuring its availability to every page template that is called, for example:

<cfset application.dsn = "projectTracker" />

You could use the Application scope variable application.dsn directly within your CFCs as the dynamic name referencing the datasource. However, this is not considered best coding practice, as you have instantly opened up your component methods to a fixed scope variable.

One of the main goals in component development is to create closed CFCs and methods that do not need to worry about whether or not a fixed variable exists.

If we refer back to the getName() function, we can see how it was originally fixed to read the first and last name from the URL scope. We resolved that issue by removing any fixed scope references and optimized the method by adding cfargument tags and the ability to pass in parameters.

We will do the same for our contacts.cfc to send in our datasource name for use in the cfquery tags.

Instead of creating a new argument for each method within the component that requires the datasource, we will create a new function that will hold the variables we need and will be open for all methods defined within the CFC to read variables from.

Creating an init() function

Let's modify our code within the contacts.cfc gallery to write the new function, init().

<cfcomponent name="contacts">

<cffunction name="init">
<cfargument name="datasource" type="string" required="true" />
<cfscript>
Variables.attributes = structNew();
Variables.attributes.dsn = arguments.datasource;
</cfscript>
<cfreturn this />
</cffunction>

<cffunction name="getContact">
<cfargument name="ID" type="numeric" default="0" />
<cfset var rstContact = "" />
<cfquery name="rstContact" datasource="projectTracker">
SELECT firstName,lastName FROM Owners
<cfif arguments.ID GT 0>
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer"
value="#arguments.ID#" />
</cfif>
</cfquery>
<cfreturn rstContact />
</cffunction>
</cfcomponent>

Listing 1.33

You can now see the init() method defined within the component. The concept of a constructor within an object is a common practice in most languages. We have included a cfargument tag with the name datasource, which will allow us to send in the name of the datasource we wish to use within this object.

Within the CFScript block, we then create a new structure that assigns the value of the datasource argument to the struct value dsn, and the structure has been assigned to the Variables scope within the CFC.

We can then amend our getContact() method and alter the datasource attribute to use the new reference, stored in the Variables scope:

<cffunction name="getContact">
<cfargument name="ID" type="numeric" default="0" />
<cfset var rstContact = "" />
<cfquery name="rstContact"
datasource="#variables.attributes.dsn#">
SELECT firstName,lastName FROM Owners
<cfif arguments.ID GT 0>
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer"
value="#arguments.ID#" />
</cfif>
</cfquery>
<cfreturn rstContact />
</cffunction>

Listing 1.34

By sending the value into the object constructor method when instantiating the component we have removed the hardcoded reference to the datasource.

The Variables scope

The Variables scope can be made available to the entire CFC by setting a value either within one of the methods or in the constructor. The value of that variable is then made available to any other method (including the constructor).

The Variables scope can be used in a similar way to storing values within the Application scope, whose values are always available throughout the entire application. This makes it ideal for sending in variables such as the datasource name.

Calling your init() function

To set the value of the datasource name into the Variables scope, we need to call the init function to pass through the argument.

In previous examples, we have already used the createObject() function to create an instance of the component. We are going to use exactly the same code, only this time we will append the init() function to the end of instantiation method call:

<!--- instantiate the object --->
<cfset objContacts = createObject('component',
'contacts').init(datasource="projectTracker") />

Listing 1.35

By doing this, we have passed our datasource name as an argument into the init() method within the contacts.cfc. The argument value is then stored within the Variables scope structure (Variables.attributes).

Values stored within the Variables scope last as long as the component instance exists, and therefore can persist between calls to methods of a CFC instance.

The Variables scope within your CFC is available to any included pages (using the cfinclude tag), and any Variables scope variables that you have defined in the included page are also available to the CFC.

The This scope

At the end of the function, we have the cfreturn tag, which we have seen before. However, this particular method is returning a different value, which is This:

<cffunction name="init">
<cfreturn This />
</cffunction>

Listing 1.36

By adding a return type of This to the cfreturn tag, you are returning the entire object, including all of its methods, variables, and data.

In the query.cfm calling page, use the cfdump tag to display the object in the browser:

<!--- dump the contacts object --->
<cfdump var="#objContacts#" />

Listing 1.37

Object-Oriented Programming in ColdFusion

As the init() method returns the object in the This scope, we are able to access the object directly using the cfdump tag.

The This scope is similar to the Variables scope due to the fact that it is 'globally' accessible to the entire CFC. In addition, the This scope is accessible outside of the CFC, so you could call and reference the values from your object within your .cfm template calling page.

For example, if we amended the code within the init() method in the CFC from using the previously mentioned Variables scope to the This scope, we could access the datasource name from our calling page:

<cffunction name="init">
<cfargument name="datasource" required="true" />
<cfscript>
This.attributes = structNew();
This.attributes.dsn = arguments.datasource;
</cfscript>
<cfreturn This />
</cffunction>

Listing 1.38

In the query.cfm, we can now output the name of the datasource from the attributes structure stored within the This scope:

<!--- dump the contacts object --->
<cfdump var="#objContacts#" />
<cfoutput>The datasource name is
#objContacts.attributes.dsn#</cfoutput>

Notice that the attributes structure is now publicly available, allowing us to access the name of the datasource directly from the CFC.

Object-Oriented Programming in ColdFusion

This highlights the difference between the Variables and This scope. When the attributes were assigned to the Variables scope, they were kept hidden from external views, despite being available to all methods within the CFC. As soon as we changed the init() method to store attributes within the This scope, the structure became a visible, 'public' variable that could be accessed outside of the CFC.

Although the This scope is a required tool for returning a complete CFC object, it is not best practice to store variables within the scope. This is because they can be accessed and altered. Unless you specifically choose to alter your object's variables in this manner, this would not be a safe development practice.

Values stored within the This scope last as long as the component instance exists, and therefore can persist between calls to methods of a CFC instance.

Summary

We have looked at ColdFusion Components, what they are and what they are for. Within this article, we have also covered:

  • The basic structure of a CFC
  • The component tags
  • Grouping functions and methods
  • How to use arguments within your methods
  • Optimizing and encapsulating your functions to improve code portability

Further resources on this subject:


About the Author :


Matt Gifford

Matt officially began life as a developer in 2000, although he used to 'develop' simple applications using BASIC Programming on his Sinclair ZX Spectrum. After creating relational databases in VBScript and hand-coding HTML pages in text editors, the obvious route was to start developing websites using dynamic data.

He is now lead developer with Fuzzy Orange Ltd, and specializes in ColdFusion, Flex, and AIR development, and is also proud to be called an Adobe Community Professional for ColdFusion. He has spoken and presented regularly at national and international conferences and online meetings, and has written tutorials and articles for online resources and UK industry magazines.

Constantly striving to learn more and update any skill set he can, he loves to read development publications and community blogs, attend conferences, and discuss issues with other members of the development community.

A keen proponent for community resources and sharing knowledge, Matt writes and releases open-source ColdFusion applications and code samples as often as he can, and can also be seen updating resources and writing articles on his blog, http://www.mattgifford.co.uk.

Books From Packt


ColdFusion 9 Developer Tutorial
ColdFusion 9 Developer Tutorial

Building Websites with ExpressionEngine 2
Building Websites with ExpressionEngine 2

Drupal 7
Drupal 7

WordPress Top Plugins
WordPress Top Plugins

jQuery 1.4 Reference Guide
jQuery 1.4 Reference Guide

Building Websites with DotNetNuke 5
Building Websites with DotNetNuke 5

Inkscape 0.48 Essentials for Web Designers
Inkscape 0.48 Essentials for Web Designers

High Availability MySQL Cookbook
High Availability MySQL Cookbook


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software