Related Topics: ColdFusion on Ulitzer

CFDJ: Article

ColdFusion Developer's Journal Special "Frameworks" Focus Issue: onTap

Using the onTap framework to reinvent the Pet Market application

You might notice the string "/index/" in the path of the images used in the home page, and you'd be correct if you guessed this string matched the name of the base template (index.cfm). In this way, I can loosely couple the branding of images for different pages without needing to add any logic to the display or layout templates to handle the variance. This way I eliminated all image logic from the layout template.

Then there's the issue of layout in general. As a developer I tend to think about every application in terms of portability. Call me stubborn; I refuse to make assumptions about an application's operating environment. This is one of the many weaknesses of the original Pet Market application, which required that you either add a custom tag path in the ColdFusion administrator or put tags in the default custom tags directory, neither of which are solutions I'm willing to accept for an application I've written.

In the case of layout, the application uses a custom tag to create the bulk of the HTML on each page. The custom tag is used with an end-tag to wrap the content of each page. For my application, this custom tag wouldn't be necessary, although much of the same structure can be reused. I copied the /extensions/customtags/wrapper.cfm template from the original application to /_components/petmarket/_layout.cfm and changed the logic for the tag internally to use the variable variables.tap.layout (which has a value of "header" or "footer") instead of using the ColdFusion native variable thistag.executionmode. Once this was done I removed the custom tag references from all the other templates in the /_components/petmarket/ directory. I also removed the head, body. and HTML tags from the layout template, since these are generated by the framework, and moved the content of the embedded CSS style tags to the template /_components/petmarket/_htmlhead/100_petmarket.css (which is included by the framework automatically in the resultant HTML).

In all honesty this isn't the way I'd normally handle layout. There's nothing wrong with it per se, although the onTap framework includes an HTML library - a set of functions for generating modular displays in HTML format with much greater flexibility - and normally I'd use this library via an XHTML custom-tag to create and cache the layout and then insert the content for each page into the generated display structures. Although this technique is more extensible, I simply didn't have time to implement it for this article (and Simon requested that I use the original application's view layer).

The original Pet Market application also puts its ColdFusion Components (CFC) in a directory not below the web root, requiring the creation of a ColdFusion mapping to target these CFCs. The onTap framework has a central repository for CFCs and a custom function for targeting these components that allows the application to be moved to any environment and configured quickly and easily (no need to change the extends attributes of the various CFCs).

For this I copied the original application's /extensions/components/petmarket/ directory to /ontap/_components/_cfc/petmarket/ then located all references to the ColdFusion native CreateObject() function and replaced them with request.tapi.getObject() (the arguments did not change). I also would have changed any references to the cfobject tag or cfinvoke tags that might use cfc paths instead of instantiated objects (and replaced their paths with request.tapi.getCFC()), although these aren't used in the original Pet Market application, making such changes unnecessary.

Finally, the Pet Market application included an Application.cfm template in its root directory (above the web root) to execute code at the beginning of each request. Although the onTap framework has its own Application.cfc for handling the various application events (onApplicationStart, onApplicationEnd, onSessionStart, onSessionEnd, etc.), the structure of this template lets us easily migrate code from other applications used in their Application.cfc or Application.cfm templates by copying the code into an appropriate subdirectory. In the case of my Pet Market migration, I copied the contents of the original Pet Market Application.cfm template to the file /ontap/_components/petmarket/_application/100_petmarket.cfm. This template will execute at the beginning of each request when the base template is in the /ontap/petmarket/ directory.

By the time this template executes, however, the framework has already instantiated the application and so the cfapplication tag is no longer necessary and I deleted it. If I wanted to change the name of the application or its timeouts or session management settings, I'd make those changes by setting variables in the structure request.tap.cf.app (i.e., request.tap.cf.app.sessionmanagement = true) using code in the template /ontap/_components/_appsettings.cfm. (This template doesn't exist in the core components archive to safeguard against overwriting your project settings when upgrading to the latest framework core version.)

At this point, I could have left the files as they were and the application should work. I prefer, however, to have the /_components/ templates a little further segregated, so I renamed each of the templates in the /_components/petmarket/ directory to _process.cfm and put them in another subdirectory with the name of the original template, i.e., /cart.cfm is renamed to /cart/_process.cfm.

Now I have my desired file structure:


ontap/
_components/
_cfc/
petmarket/
petmarket/
_application/
_htmlhead/
_images/
_layout.cfm
about/_process.cfm
cart/_process.cfm
index/
_images/
_process.cfm
etc/_process.cfm
petmarket/
about.cfm
cart.cfm
index.cfm
etc.cfm
Object-Relational Mapping (ORM) and Data Access Objects (DAO)
The next task I tackled after moving and renaming all the original Pet Market files was to reinvent the manner in which the application accesses data. To be fair this wasn't entirely necessary. I could simply have left all the ad hoc SQL queries in place and claimed the application complete, although since I'm something of a perfectionist I wouldn't be happy with the application's existing structure for data access. The authors created a request variable (request.petmarketdb) for targeting the database with cfquery tags, which is a good start and where most applications end their encapsulation of datasources (not to be confused with encapsulation of SQL queries), although I'm personally unsatisfied with this minimalist approach. Instead, I created a Datasource Name (DSN) structure in the onTap framework for holding information about multiple databases, where I registered my Pet Market DSN (request.tap.dsn.petmarket).

I registered this structure in the request scope using the /ontap/_components/_appsettings.cfm template for general application configuration and then set about the task of replacing all the application's ad-hoc SQL queries with the onTap framework SQL-abstraction tools. By default these tools use the "primary" DSN (request.tap.dsn.primary) so if I want my SQL-abstraction code to access my new Pet Market DSN I have to include the DSN attribute or argument in each of the custom tags or function calls used to access this abstraction layer.

One way to reduce the amount of code needed to accomplish this task is to use a datasource.cfc provided by the framework and instantiate it with the name of the DSN I want it to use. Because I don't want to recreate this CFC on each request where I might need it, I create it in the application scope (application.petmerket.datasource) when the application starts by putting the object instantiation in the template /ontap/_components/_appstart/100_petmarket.cfm.

Once I started looking more deeply into the data access used in the Pet Market application, I realized that most of it could be replaced with standardized Object Relational Mapping (ORM) techniques. For this I used the onTap framework's dynamic inheritance tool in the soft constructor for most of the existing CFCs to extend the framework's Data Access Object (DAO) component (request.tapi.cfcExtend(this,"dao")) and eliminated a large amount of code from the CFCs that simply put data returned from ad hoc SQL queries into the "this" scope or a structure in the "this" scope.

More Stories By Isaac Dealey

Isaac Dealey has worked with ColdFusion since 1997 (version 3) for clients from small businesses to large enterprises, including MCI and AT&T Wireless. He evangelizes ColdFusion as a volunteer member of Team Macromedia, is working toward becoming a technical instructor, and is available for speaking engagements.

Comments (2) View Comments

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


Most Recent Comments
SYS-CON Brazil News Desk 02/01/06 03:38:19 PM EST

I must admit to having been excited at the prospect of the Pet Market frameworks project when Simon proposed it to us at the Fusebox & Frameworks Conference in September. I once tried to do something similar by creating a small blog application using the three popular frameworks that I was aware of at the time (Fusebox 3-4 and Mach-II) and the onTap framework.

SYS-CON India News Desk 02/01/06 02:49:28 PM EST

I must admit to having been excited at the prospect of the Pet Market frameworks project when Simon proposed it to us at the Fusebox & Frameworks Conference in September. I once tried to do something similar by creating a small blog application using the three popular frameworks that I was aware of at the time (Fusebox 3-4 and Mach-II) and the onTap framework.