Skip to content

Primer Part 3: Flip This Code

thofrey edited this page Apr 1, 2014 · 7 revisions

Table of Contents

  1. Preparation
  2. Let's see how it looks
  3. Other Chapters

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.

Preparation

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.

  1. Rename the version 0 mach-ii.xml file to mach-ii.00.xml
  2. Rename mach-ii.01.xml to mach-ii.xml

Mach-II should be loading version 1 of mach-ii.xml:

  1. Using "/01/" folders
  2. Added <event-handler>: showCategories, showCompanies, showContacts
  3. 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.

http://localhost/mach-ii-primer/classic/

From the Homepage, you can access three main areas:

  1. Contacts
  2. Companies
  3. Categories

Click into each area and you'll find many common functions:

  1. List entries in the database
  2. View details of an entry
  3. 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>

Let's see how it looks

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.

  1. Rename the version 1 mach-ii.xml file to mach-ii.01.xml
  2. Rename mach-ii.02.xml to mach-ii.xml
  3. Reload the page (event) in your browser

Other Chapters

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).

Special thanks to Adrian J. Moreno of IKnowKungFoo for contributing this series of primers.

Clone this wiki locally