-
Notifications
You must be signed in to change notification settings - Fork 24
Primer Part 3: Flip This Code
I'm hooked on those real estate shows where investors find old houses and fix them up. Once they've gone through the house and made plans, they rip out what they don't want or need, keep what they can reuse and then begin new construction. Using Mach-II, we can flip old Coldfusion code into modern, object-oriented applications.
If you haven't downloaded the project code yet, click here.
Make sure that your Mach-II application is setup to always reload the configuration file. Depending on your setup, this is done in either Application.cfc or index.cfm:
<cfset MACHII_CONFIG_MODE = 0 />
We'll be using the Version 1 files now.
- Rename the version 0 mach-ii.xml file to mach-ii.00.xml
- Rename mach-ii.01.xml to mach-ii.xml
Mach-II should be loading version 1 of mach-ii.xml:
- Using "/01/" folders
- Added <event-handler>: showCategories, showCompanies, showContacts
- Added <page-view>: categoryList, companyList, contactList
Go through the old application
*cue music*
Located in the "/classic/" folder, this application is a standard Contact Management System, built in a style common to the late 90's. Let's go in the front door and see what's in there.
From the Homepage, you can access three main areas:
- Contacts
- Companies
- Categories
Click into each area and you'll find many common functions:
- List entries in the database
- View details of an entry
- Add, edit or delete an entry
*end music*
Let's say you got lucky and the Business Requirements and possibly the Technical Specifications have already been prepared. You're just concerned with tearing apart the old application and building the new.
You know the sections of the application and their functionalities, so what code you can get rid of and what code you can keep?
Open up /classic/contacts.cfm and look at the code.
<html>
<head>
<title>Classic CF Demo - Contact List</title>
</head>
<body>
<cfinclude template="includes/header.cfm">
<table width="770" cellspacing="0" cellpadding="4" align="center" border="0">
<tr>
<td valign="top" bgcolor="Silver" width="100">
<cfinclude template="includes/lhs_navigation_menu.cfm">
</td>
<td valign="top">
<h2>Contact List</h2>
<p><a href="contact_form.cfm?CONTACT_ID=0">Add a Contact</a></p>
<cfquery name="qContacts" datasource="#request.DSN#">
SELECT
CONTACT_ID,
CONTACT_FIRST_NAME,
CONTACT_LAST_NAME
FROM
CF_CONTACTS
ORDER BY
CONTACT_LAST_NAME, CONTACT_FIRST_NAME
</cfquery>
<ul>
<cfoutput query="qContacts">
<li>
[ <a href="contact_form.cfm?CONTACT_ID=#qContacts.CONTACT_ID#">Edit</a> ]
<a href="contact_detail.cfm?CONTACT_ID=#qContacts.CONTACT_ID#">
#qContacts.CONTACT_LAST_NAME#,
#qContacts.CONTACT_FIRST_NAME#
</a>
</li>
</cfoutput>
</ul>
</td>
</tr>
</table>
<cfinclude template="includes/footer.cfm">
</body>
</html>
Scary, huh?
HTML, CFML and SQL all in the same file. <cfinclude>s to pull in the header, footer and side navigation. And (ugh!) table based layout.
Why is the <cfquery> in the middle of the page? Because the query is being output right after it, that's why. (Oh come on, you knew why. You've done the same thing.)
Plan out construction
Let's begin by moving the basic "list" pages from the classic application to the Mach-II skeleton.
/classic/index.cfm
<page-view name="homePage" page="/views/home.cfm" />
/classic/contacts.cfm
<page-view name="contactList" page="/views/contacts/01/contacts.cfm" />
/classic/companies.cfm
<page-view name="companyList" page="/views/companies/01/companies.cfm" />
/classic/categories.cfm
<page-view name="categoryList" page="/views/categories/01/categories.cfm" />
/classic/includes/lhs_navigation_menu.cfm
<page-view name="lhsMenu" page="/views/layout/lhs_navigation_menu.cfm" />
/classic/includes/header.cfm
<page-view name="header" page="/views/layout/header.cfm" />
/classic/includes/footer.cfm
<page-view name="footer" page="/views/layout/footer.cfm" />
After looking through the classic files, let's copy the <html>, <head> and opening <body> tags into header.cfm, copy the closing <body> and <html> tags into footer.cfm. Now we'll create a template.cfm and page-view to handle positioning the side navigation and page content. (Still in tables as I don't want to get into CSS positioning in this primer.)
<page-view name="template" page="/views/layout/template.cfm" />
<!--- file --->
<table width="770" cellspacing="0" cellpadding="4" align="center" border="0">
<tr>
<td id="sideNavigation">
<cfoutput>#event.getArg("sideBar")#</cfoutput>
</td>
<td id="mainContent">
<cfoutput><h3>#event.getArg("pageTitle")#</h3></cfoutput>
<cfoutput>#event.getArg("mainContent")#</cfoutput>
</td>
</tr>
</table>
Finally, we create new event-handlers for the three main lists, based on home
<event-handler event="showCategories" access="public">
<view-page name="header" />
<view-page name="lhsMenu" contentArg="sidebar" />
<view-page name="categoryList" contentArg="mainContent" />
<view-page name="template" />
<view-page name="footer" />
</event-handler>
<event-handler event="showCompanies" access="public">
<view-page name="header" />
<view-page name="lhsMenu" contentArg="sidebar" />
<view-page name="companyList" contentArg="mainContent" />
<view-page name="template" />
<view-page name="footer" />
</event-handler>
<event-handler event="showContacts" access="public">
<view-page name="header" />
<view-page name="lhsMenu" contentArg="sidebar" />
<view-page name="contactList" contentArg="mainContent" />
<view-page name="template" />
<view-page name="footer" />
</event-handler>
Open the application under Mach-II
http://localhost/mach-ii-primer/m2/
and click on Contacts in the left hand navigation.
You should have received an error: File not found: /mach-ii-primer/m2/contacts.cfm.
We need the left hand navigation menu to link to events instead of files. I've created a version that does that already, but we need the events we've already defined to use that altered CFM file instead of the original version that links to files.
Open up mach-ii.xml and find the definition for the "lhsMenu" page-view
<page-view name="lhsMenu" page="/views/layout/lhs_navigation_menu.cfm" />
Change it to
<page-view name="lhsMenu" page="/views/layout/m2_lhs_navigation_menu.cfm" />
Save mach-ii.xml and reload the home page (event) in the browser. The left hand navigation now links to events instead of files.
By updating one line of XML, you've updated four events.
Now click on Contacts so we can see the list under Mach-II.
Oops. Another error: Could not find the included template includes/header.cfm.
While we've requested the Home event, Mach-II encountered an error and announced the exceptionEvent in order to display the error message and details.
You'll notice that there are two headers rendered on the page before the error message. This is beacuse the "header" in home was compiled before the error occurred, it was then appended to the arguments (content) of exceptionEvent. exceptionEvent also has the "header" defined, but at this point the one from home is rendered HTML so the final compiled event shows both.
Begin Demolition
Let's look at the contact list file that we copied over from the procedural application:
<!--- /m2/views/contacts/01/contacts.cfm --->
<html>
<head>
<title>Classic CF Demo - Contact List</title>
</head>
<body>
<cfinclude template="includes/header.cfm">
<table width="770" cellspacing="0" cellpadding="4" align="center" border="0">
<tr>
<td valign="top" bgcolor="Silver" width="100">
<cfinclude template="includes/lhs_navigation_menu.cfm">
</td>
<td valign="top">
<h2>Contact List</h2>
<p><a href="contact_form.cfm?CONTACT_ID=0">Add a Contact</a></p>
<cfquery name="qContacts" datasource="#request.DSN#">
SELECT
CONTACT_ID,
CONTACT_FIRST_NAME,
CONTACT_LAST_NAME
FROM
CF_CONTACTS
ORDER BY
CONTACT_LAST_NAME, CONTACT_FIRST_NAME
</cfquery>
<ul>
<cfoutput query="qContacts">
<li>
[ <a href="contact_form.cfm?CONTACT_ID=#qContacts.CONTACT_ID#">Edit</a> ]
<a href="contact_detail.cfm?CONTACT_ID=#qContacts.CONTACT_ID#">
#qContacts.CONTACT_LAST_NAME#,
#qContacts.CONTACT_FIRST_NAME#
</a>
</li>
</cfoutput>
</ul>
</td>
</tr>
</table>
<cfinclude template="includes/footer.cfm">
</body>
</html>
Since we have page-views for the header, footer and side navigation, we can remove the <cfinclude>s. The "template" page-view means we can remove the table as well. Finally, using an event-arg, we can place the page title in the XML.
All we need is the actual page content.
<!--- /m2/views/contacts/02/contacts.cfm --->
<p><a href="contact_form.cfm?CONTACT_ID=0">Add a Contact</a></p>
<cfquery name="qContacts" datasource="#request.DSN#">
SELECT
CONTACT_ID,
CONTACT_FIRST_NAME,
CONTACT_LAST_NAME
FROM
CF_CONTACTS
ORDER BY
CONTACT_LAST_NAME, CONTACT_FIRST_NAME
</cfquery>
<ul>
<cfoutput query="qContacts">
<li>
[ <a href="contact_form.cfm?CONTACT_ID=#qContacts.CONTACT_ID#">Edit</a> ]
<a href="contact_detail.cfm?CONTACT_ID=#qContacts.CONTACT_ID#">
#qContacts.CONTACT_LAST_NAME#,
#qContacts.CONTACT_FIRST_NAME#
</a>
</li>
</cfoutput>
</ul>
Once the same changes are made to companies.cfm and categories.cfm, you should be able to load the list pages for all three sections of the application correctly.
You can see these changes in action by using the version 2 files.
- Rename the version 1 mach-ii.xml file to mach-ii.01.xml
- Rename mach-ii.02.xml to mach-ii.xml
- Reload the page (event) in your browser
We've moved code that manages layout and HTML content or the presentation layer into Mach-II components that are controlled by the configuration file. However, they still contain queries.
In order to move queries that return multiple records from the View to the Model, we need to introduce Gateway Objects using Coldfusion Components (CFCs).
- Mach-II Primer Series Navigation
- Part 1 - Files versus Events
- Part 2 - Variables versus Arguments
- Part 3 - Flip This Code
- Part 4 - Using Gateways to manage record sets
- Part 5 - Mediating events with Listeners
- Part 6 - Getting the details with Beans and DAOs
- Part 7 - Processing data with Beans and DAOs using Event-Filters
Special thanks to Adrian J. Moreno of IKnowKungFoo for contributing this series of primers.