Create Scalable Applications with ColdFusion Components

This article presents an introduction to ColdFusion components (CFCs). For developers seeking to take their skills to the next level, CFCs are an invaluable addition to the toolkit, allowing the adoption of a more professional approach to web application development.

Ushered in with ColdFusion MX 6, CFCs have enjoyed strong support from the developer community ever since. These days, ColdFusion 8 provides both significant and subtle improvements to the way CFCs work, which include major new features such as close Ajax integration, not to mention greatly enhanced performance.

Being able to develop using CFCs allows us to write highly maintainable and flexible ColdFusion applications and reuse more code. Even better, it also permits us to leverage great capabilities in ColdFusion, both new and existing, such as automatic web service generation and powerful Ajax tags.

This article will cover the following topics:

  • what a CFC is
  • why CFCs are useful and worth learning
  • how to write a CFC
  • how and where to start using CFCs in applications
  • an introduction to other great features in ColdFusion that use CFCs
  • tips for developers new to CFCs
Requirements

software

sample code

A set of sample code for using the sample BookClub database that shipped with CF8 is available here.

prerequisite knowledge

Users are assumed to be familiar with basic ColdFusion development, including user-defined functions — refer to the ColdFusion documentation if you need a general refresher or specific reminder.

What Is a ColdFusion Component?

ColdFusion components (CFCs) are a way of grouping related functions and data together in the one file, regardless of where they’re needed throughout our application. Some CFCs model a particular piece of data; others provide functions for accessing external data. We’ll focus on the latter in this article.

When we work with ColdFusion components, we’re really using a form of object oriented programming (OOP). If you haven’t been involved with any OOP before, don’t feel intimidated. You don’t need to be familiar with the object oriented features of ColdFusion in order to read, understand, and make use of this article and ColdFusion components.

Why Are ColdFusion Components Useful?

It is possible to write sophisticated applications without components, but using CFCs in your applications help make them more maintainable and flexible while allowing greater code reuse. For anything other than a throwaway application, CFCs allow for better, more reliable, and more scalable applications.

These advantages arise from separating presentation logic from the business rules and data access logic. Code that generates direct output — such as ordinary HTML markup and ColdFusion tags like CFFORM — tends to vary considerably from page to page. Business and data access logic, however, tends to remain constant.

CFCs allow us to capture this business and data access logic and separate it from the page markup, in effect creating a separate presentation and business layer in our application. Changing the presentation layer is less likely to affect the business layer, and vice versa. And the changes we make anywhere are likely to be smaller, since we’re no longer repeating ourselves unnecessarily. This is a good first step towards developing better architecture for our applications.

As an additional advantage, working with ColdFusion components within our applications also allows us to take advantage of some of the more powerful features of ColdFusion, such as automatic web-service generation, Ajax controls, and the application framework.

The Sample Application

Over the course of this article, we’ll use a sample application to see how CFCs can be used in applications. Using this BookClub database, we’ll work with a web application that, apart from tracking books and authors, also allows the user to read and post blog articles. Let’s dub our web site "BookBlogs."

This web site begins its life as a traditional page-based CFML application. Nearly all ColdFusion developers start with applications such as this one. This table of excerpts from the bookblogs, members, and blogs folders should seem familiar:

/bookblogs /bookblogs/members /bookblogs/blogs
application.cfm memberList.cfm blogEntryList.cfm
index.cfm profile.cfm blogEntry.cfm
editProfileForm.cfm newBlogEntryForm.cfm
updateProfile.cfm addBlogEntry.cfm/
register.cfm

Other folders in the application have a similar structure. Apart from separate include files for headers and footers (hidden away in the /bookblogs/layout folder), our application is otherwise made up of a CFM file for each page the user can visit. Each of these CFM files incorporates all the required CFML code to apply business logic, access the database, and output the results. This can be seen by examining the contents of the blogEntryList.cfm file:

<cfset pageTitle = "Blogs" /> 

<cfinclude template="../layout/layoutQueries.cfm">
<cfinclude template="../layout/header.cfm">

<!--- Retrieve All Blog Entries --->
<cfquery datasource="#application.dsn#" name="blogEntries">
 SELECT * FROM BlogEntries
 ORDER BY Posted DESC
</cfquery>

<h2>All Blog Entries</h2>
<table width="90%">
 <tr>
   <th>Date</th>
   <th>User</th>
   <th>Title</th>
 </tr>
 <cfoutput query="blogEntries">
   <tr>
     <td>#dateFormat(posted)#</td>
     <td>#username#</td>
     <td>#title#</td>
   </tr>
 </cfoutput>
</table>

<cfinclude template="../layout/footer.cfm">

As we can see in the figure below, the blogEntryList.cfm page queries the database for a complete listing of blog posts and displays them all in a simple HTML table. There’s nothing challenging about this code, except for the fact that the same SQL statement (and therefore business rule) appears with slight variations in three pages across the application.

Firstly, the blogEntryList.cfm page displays a listing of all blog entries, as shown.

Our BlogEntryList page

As you would expect of a blog site, the homepage, or more specifically our index.cfm page, shows a list of the most current blog entries.

Our index.cfm page

And the profile.cfm page shows the blog entries for a particular author.

Our profile.cfm page

Coding such a simple SELECT statement in three places during development might not seem too arduous, but it’s generally after the source code leaves our hands and must be supported that problems occur. When we return to fix a bug, or add an approval feature, integrate another data source, or simply apply the client’s latest request, we find that we need to modify and test each file. Without good documentation and a simple code base, it can be problematic to even remember which CFM files we need to update.

Naturally, there is a better way: define a BlogManager ColdFusion component to capture all blog-related business rules and data access. And that’s exactly what we’re going to do.

Defining a CFC

The first step in integrating a ColdFusion component into our existing application is to define it. Every CFC should have clearly understood roles and responsibilities. In the case of our BlogManager CFC, we want it to capture all of the business rules and data access logic required for the blog functionality.

Once this important decision about our component is made, implementing the component in ColdFusion is easy. Each CFC is defined in a single file. The filename is the desired name of the component with a .cfc extension. In this case, we’ll define our component in a file named BlogManager.cfc.

Our CFC can live nearly anywhere on our file system, but if it’s not in the same directory as the code that will use it, we need to refer to it using a "dot notation" syntax from the web root directory or from a mapping created in the ColdFusion Administrator.

In this instance we’ll place the BlogManager CFC in a subfolder called components beneath our main application folder bookblogs, which is directly below the web root directory. We therefore use the full component name of bookblogs.components.BlogManager to refer to the component from other locations. Separating your CFCs from other files in the web application is usually a good idea — it encourages reuse across many different pages, regardless of their location.

Inside our .cfc file we define our component. Our BlogManager CFC will initially look something like this:

<cfcomponent output="false" hint="Manages all Blog-related business rules  
   and data access">  
 <cfset this.dsn = application.dsn />  
 <cffunction name="getBlogEntries" access="public" returnType="query"  
     output="false"  
     hint="Returns a query containing all blog entries in  
           descending order by posted date">  
 
   <!--- Retrieve All Blog Entries --->  
   <cfquery datasource="#this.dsn#"    
       name="blogEntries">  
     SELECT * FROM BlogEntries  
     ORDER BY Posted DESC  
   </cfquery>              
   <cfreturn blogEntries />  
 </cffunction>  
</cfcomponent>

Surrounding all of the code in our component is a single set of <cfcomponent> tags. Within these tags, we can write plain CFML in the form of independent statements and familiar user-defined functions (UDFs). We have defined two attributes on the opening <cfcomponent> tag: output and hint. Although optional, including both of these attributes is considered good practice.

Setting the output attribute to false prevents extraneous code within the component from being included in your page output. The hint attribute allows you to describe the purpose of your CFC. This does not affect the operation of your component but makes it easier for other developers to determine the role and function you have chosen for your component.

The first element within the <cfcomponent> tag is a single <cfset> statement. Any code outside of functions in your component — such as this <cfset> — will be executed whenever you first create or invoke it. This specific statement stores the name of the data source for the database, using the special this scope, for later use by the component. As your experience with CFCs grows, you’ll find a lot of uses for storing data in your components.

Following the <cfset> statement is our function getBlogEntries defined by the <cffunction> tag. Functions inside of components are often called methods. Apart from the mandatory name attribute, the other optional attributes returnType, output, hint, and access are defined. returnType, output, and hint are standard <cffunction> attributes that have the same meaning here as when used with normal UDFs.

The returnType attribute specifies the type of data the function will return. You can set this to a simple or complex data type, such as numeric, string, or struct. We can even use a component name. If we supply this value, ColdFusion will ensure the value returned from the function matches this data type. The output and hint attributes are identical to the same attributes on the <cfcomponent> tag. The access attribute determines which code can run the function and is fully discussed in the "Security Considerations" note included later in the article. Including these attributes is optional, but is considered best practice.

Within the getBlogEntries function, we use a <cfquery> tag to pass an SQL query to the database. Notice how we can access this .dsn variable to determine the data source name. The query in this example is fixed, but you could just as easily generate the query dynamically by using arguments passed into the method. The query result is then returned to the caller with a <cfreturn> tag.

This is just the beginning of the possibilities offered by our new component. At the moment it can only retrieve a list of blog entries. Since we wanted the BlogManager component to handle all business logic and data access relating to blogs, we also need functions to create, read, update, and delete blog entries. Although we won’t be creating them in this article, these methods can be found in the source code download.

For the moment, however, let’s see how we can use the BlogManager component and the getBlogEntries function in our applications.

Using CFCs in Your ColdFusion Applications

Within a page that needs to use the functionality defined by the component, we can execute the function with the <cfinvoke> tag. The <cfinvoke> tag is a powerful tag that can create components from their definition files and execute specific functions. All we need to do is specify the component, the method (remember, this is just another name for function) and where we want the result stored. Our listing of all of the available blog posts (listBlogEntries.cfm) can be changed to use the component as shown below:

<cfset pageTitle = "Blogs" />  
<cfinclude template="../layout/layoutQueries.cfm">  
<cfinclude template="../layout/header.cfm">  
 
<cfinvoke component="bookblogs.component.BlogManager"  
   method="getBlogEntries"  
   returnvariable="blogEntries" />  
 
<h2>All Blog Entries</h2>  
<table width="90%">  
 <tr>  
   <th>Date</th>  
   <th>User</th>  
   <th>Title</th>  
 </tr>  
 <cfoutput query="blogEntries">  
   <tr>  
     <td>#dateFormat(posted)#</td>  
     <td>#username#</td>  
     <td>#title#</td>  
   </tr>  
 </cfoutput>  
</table>  
 
<cfinclude template="../layout/footer.cfm">

As the listing shows, we have completely removed the previous <cfquery> and replaced it with the <cfinvoke> tag. The query that is returned the blogEntries variable which we use to build the HTML table is exactly the same. We’ve saved a couple of lines in our page, but more importantly, our other pages can use this function too, after only a small amount of modification.

Our member profile page displays a list of blog entries for that specific member, and our homepage only displays a limited amount of the latest entries. If we add a couple of arguments to our getBlogEntries function, we can make the SQL dynamic, and make the function useful enough to be called from all of the pages that need to retrieve blog entries:

<cffunction name="getBlogEntries" access="public" returnType="query"  
     output="false"  
     hint="Returns a query containing all blog entries,  
           optionally filtered by memberId and limited by to set  
           number of entries (supplied from the most recent) in  
           descending order by posted date">  
   <cfargument name="filterByMemberId" type="numeric" required="false"  
       default="0"  
       hint="If supplied, only blog entries by this member  
             are returned" />  
   <cfargument name="maxEntries" type="numeric" required="false"              
       default="9999"  
       hint="If supplied, limits the total number of blog entries  
             returned to this amount"  />  
                 
   <!--- Retrieve Blog Entries, optionally filtered by member and  
         maximum number of rows returned --->  
   <cfquery datasource="#this.dsn#" name="blogEntries"  
       maxRows="#arguments.maxEntries#">  
     SELECT * FROM BlogEntries, Members  
     WHERE BlogEntries.username = members.email  
     <cfif arguments.memberId GT 0>  
       AND memberId = #arguments.filterByMemberId#  
     </cfif>  
     ORDER BY Posted DESC  
   </cfquery>              
   <cfreturn blogEntries />  
 </cffunction>

Here we are using optional arguments, defined using <cfargument> tags, to dynamically change our SQL statement each time the method is invoked. The filterByMemberId argument is checked within the query; if it differs from the default, an additional WHERE clause is applied to our SQL statement. The maxEntries argument, on the other hand, is always applied to the maxRows attribute of the <cfquery> tag, but with a very large default value.

To call the function from our member profile page (profile.cfm), we would use the following syntax for <cfinvoke>:

<cfinvoke component="bookblogs.components.BlogManager"  
   method="getBlogEntries"  
   returnvariable="theirBlogEntries">  
 <cfinvokeargument name="filterByMemberId" value="#url.memberId#" />  
</cfinvoke>

Notice we are now using a nested tag, <cfinvokeargument>, to pass the member ID to the filterByMemberId argument of our function. Similarly, on our homepage (index.cfm) we now invoke the component function using the maxEntries argument to limit the results returned:

<cfinvoke component="bookblogs.components.BlogManager"  
   method="getBlogEntries"  
   returnvariable="recentBlogEntries">  
 <cfinvokeargument name="maxEntries"  
     value="#application.numberOfBlogEntriesOnHomePage#" />  
</cfinvoke>

If neither argument is provided, the function still behaves as it originally did, which is suitable for use on the blog entry listing page.

NOTE: Security Considerations
Earlier in the article I mentioned the access attribute that appears on the <cffunction> tag. This attribute controls where our CFC can be invoked from. ColdFusion allows every function in a component to individually set this attribute to one of four different values explained below:

  • Private: the function can only be run from within the component.
  • Package: the function can only be run from within the component or from other components in the same folder.
  • Public: the function can run from any template or component on the server. This is the default.
  • Remote: the function can be accessed directly from external processes, including Ajax scripts, web service requests, Flex and Flash remoting, and even directly in the address bar of the browser.

As a general rule, you should always set the level of access to the lowest possible value that meets your requirements.

Completing BlogManager

At this point the advantages of consolidating our business and data access logic in a single component should start to become clear. Any changes to the underlying data or business rules can be effectively hidden from the rest of the application. If we were asked to filter out unapproved blog entries from all pages, for example, we would simply change the SQL of the getBlogEntries function. Likewise, if we decided to incorporate news feeds from independent book-related blogs using RSS, the change could be made without affecting the other application pages.

To complete the BlogManager component we need to add methods to create, read, and update blog entries. We also need functions to add comments to blog entries and work with the list of available blog categories.

The BookBlogs site contains pages devoted to books, authors, and member information. Each of these sets of information have their own business and data access logic that can benefit from being encapsulated into ColdFusion components such as BookManager and MemberManager.

The entire site, including a completed version of BlogManager, BookManager, MemberManager, and other ColdFusion components can be found within the source code download. The source code also has additional hints and tips included as comments.

So far we have centralized our business logic and data access throughout our application, and probably saved ourselves countless hours in its future support -- but now it's time to think even bigger!

Do More with CFCs

In addition to the advantages we have already explored, the highly structured nature of CFCs allows ColdFusion to integrate very tightly with the CFC definitions within applications to provide impressive capabilities with little effort.

Using CFCs we can:

  • automatically publish web services by changing the access level of a function to remote
  • bind rich Ajax controls directly to our CFCs
  • respond to events like application start, session start, and even missing templates by using an application.cfc file
  • design applications that communicate with external services and agents such as JMS, SMS, Flex Messaging, and Flash Remoting
  • effectively use community CFC-based frameworks that automate database code and more

Covering even the basics of these topics would take several more articles!

Tips for Using CFCs

ColdFusion components are easy to implement and use. Perhaps the toughest part is deciding how, where, and when to use components within your applications. This article has showcased a very narrow usage of CFCs within your applications, but there are no hard and fast rules. Below are my own personal tips on how to use CFCs effectively:

  • Other forms of reuse, such as page templates, custom tags, and individual UDFs still have their uses. Where CFCs really shine is in capturing specific business rules and data access logic. Custom Tags and UDFs, on the other hand, are ideal for generic output and data manipulation tasks that aren't specific to any business need.
  • Organizing CFCs around independent related data and functionality is important. Giving your CFCs names that reflect concrete objects, well-defined business concepts, and even job titles can help keep CFCs focused on their set of responsibilities.
  • Document your CFCs using the hint attribute on both <cfcomponent> and <cffunction> tags. These hints, although optional, are useful for both reviewing source code manually and through the component explorer. Documentation is important when others must work with your code or when you'll need to be able to understand it after development.
  • Learn more. ColdFusion components are your gateway into both a better understanding of object oriented programing and the ability to utilize the most powerful features of ColdFusion.
Where to Go from Here

I recommend the following resources for learning more about ColdFusion components:

Conclusion

During this article we've discovered what ColdFusion components are, how they are defined, and how other pages can be designed to use them. While this is enough to get you started, we've barely scratched the surface of what CFCs are capable of!

Ultimately, CFCs help ColdFusion developers write maintainable and flexible applications that can grow and evolve over time. Even better, they save you work right away through code reuse, and later, through using the advanced features of ColdFusion such as Ajax controls and web service generation. You'll thank yourself for taking the time to get to know them!

Remember you can download the complete code archive for the examples presented in this article.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

No Reader comments

Comments on this post are closed.