Skip to content

Primer Part 7: Processing Data with Beans and DAOs Using Event Filters

thofrey edited this page Apr 1, 2014 · 9 revisions

Table of Contents

  1. Create a Company
  2. Creating the showCompanyForm event
  3. Filtering data
  4. Procedural version
  5. Mach-II version
  6. What is a Mach-II Event Filter?
  7. Other Chapters

We've seen how to convert data list and data detail pages from procedural code to Mach-II events. Now we need to refactor how we build form and action pages.

Create a Company

From a data list page, we generally have the options to Add a New Item, View Item Details or Edit Item.

Clicking on "Add a Company", we get a new Company form.

Here's the procedural version of this page:

/mach-ii-primer/classic/company_form.cfm

    <html>
    <head>
        <title>Classic CF Demo - Company Form</title>
    </head>

    <body>

        <cfinclude template="includes/header.cfm">

        <cfparam name="url.COMPANY_ID" type="numeric" default="0" />

        <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>Company Form</h2>

                    <cfquery name="qCompanies" datasource="#request.DSN#">
                    SELECT
                        COMPANY_ID,
                        COMPANY_NAME,
                        COMPANY_ADDRESS_ONE,
                        COMPANY_ADDRESS_TWO,
                        COMPANY_CITY,
                        COMPANY_STATE,
                        COMPANY_ZIP,
                        COMPANY_PHONE_MAIN
                    FROM
                        CF_COMPANIES
                    WHERE
                        COMPANY_ID = '#url.COMPANY_ID#'
                    </cfquery>

                    <script type="text/javascript">
                    function validateCompany()
                    {
                    var f = document.companyData;
                    var err = "";

                    if (f.COMPANY_NAME.value == "")
                    {
                        err += "Please enter a Company Name.\n";
                    }

                    if (f.COMPANY_ADDRESS_ONE.value == "")
                    {
                        err += "Please enter an Address.\n";
                    }

                    if (f.COMPANY_CITY.value == "")
                    {
                        err += "Please enter the City.\n";
                    }

                    if (f.COMPANY_STATE.value == "")
                    {
                        err += "Please enter the State.\n";
                    }

                    if (f.COMPANY_ZIP.value == "")
                    {
                        err += "Please enter the Zip Code.\n";
                    }

                    if (f.COMPANY_PHONE_MAIN.value == "")
                    {
                        err += "Please enter the Main Phone Number.\n";
                    }

                    if (err != "")
                    {
                        alert(err);
                        return false;
                    }

                    return true;
                    }
                    </script>

                    <cfoutput>
                    <form name="companyData"
                        action="company_form_process.cfm"
                        method="post"
                        onSubmit="return validateCompany()">

                    <input type="hidden" name="COMPANY_ID" value="#qCompanies.COMPANY_ID#" />

                    <p>Company Name: <input type="text" name="COMPANY_NAME" value="#qCompanies.COMPANY_NAME#" /></p>
                    <p>Address Line 1: <input type="text" name="COMPANY_ADDRESS_ONE" value="#qCompanies.COMPANY_ADDRESS_ONE#" /></p>
                    <p>Address Line 2: <input type="text" name="COMPANY_ADDRESS_TWO" value="#qCompanies.COMPANY_ADDRESS_TWO#" /></p>
                    <p>City: <input type="text" name="COMPANY_CITY" value="#qCompanies.COMPANY_CITY#" /></p>
                    <p>State: <input type="text" name="COMPANY_STATE" value="#qCompanies.COMPANY_STATE#" /></p>
                    <p>Zip: <input type="text" name="COMPANY_ZIP" value="#qCompanies.COMPANY_ZIP#" /></p>
                    <p>Main Phone: <input type="text" name="COMPANY_PHONE_MAIN" value="#qCompanies.COMPANY_PHONE_MAIN#" /></p>

                    <p>
                        <cfif url.COMPANY_ID eq 0>
                            <input type="submit" name="addCompany" value="Add Company" />
                        <cfelse>
                            <input type="submit" name="editCompany" value="Edit Company" />
                            <input type="submit" name="deleteCompany" value="Delete Company" />
                        </cfif>
                        <input type="button" name="cancel" value="Cancel" onClick="history.back();" />
                    </p>

                    </form>
                    </cfoutput>
                </td>
            </tr>
        </table>

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

    </body>
    </html>

The link to "Add a new Company" passes COMPANY_ID=0 in the querystring, so the query qCompanies returns an emtpy record set, so the form fields are all populated with empty strings.

We've already refactored the layout HTML into Mach-II <page-view>s. All that's left are three major sections of code:

  1. The query qCompanies.
  2. Javascript to validate the form's contents.
  3. The form itself.

Creating the showCompanyForm event

First we need a query that returns a single company record. We already have this code in the CompanyListener, which uses the Company bean and CompanyDAO.

mach-ii-primer/m2/model/companies/05/CompanyListener.cfc

    <notify listener="CompanyListener" method="getCompanyDetail" resultArg="companyBean" />
    <cffunction name="getCompanyDetail"
        access="public"
        output="false"
        returntype="Company"
        hint="returns a populated Company Bean.">

        <cfargument name="event" type="MachII.framework.Event" required="true" />

        <cfset var company = createObject("component", "Company").init( arguments.event.getArg("COMPANY_ID") ) />
        <cfset var companyDAO = createObject("component", "CompanyDAO").init( DSN = request.DSN ) />

        <cfset companyDAO.read(company) />
        <cfreturn company />
    </cffunction>

Next we need to move the Company form and Javascript to its own CFM file.

    <page-view name="companyForm" page="/views/companies/05/company_form.cfm" />

The values of the form fields are now being populated by the contents of the Company bean.

mach-ii-primer/views/companies/05/company_form.cfm

    <cfset company = event.getArg("companyBean") />

    <cfoutput>
        <form name="companyData" action="index.cfm?event=processCompanyData" method="post" onSubmit="return validateCompany()">

            <input type="hidden" name="COMPANY_ID" value="#company.getCompanyID()#" />

            <p>Company Name: <input type="text" name="COMPANY_NAME" value="#company.getName()#" /></p>
            <p>Address Line 1: <input type="text" name="COMPANY_ADDRESS_ONE" value="#company.getAddressOne()#" /></p>
            <p>Address Line 2: <input type="text" name="COMPANY_ADDRESS_TWO" value="#company.getAddressTwo()#" /></p>
            <p>City: <input type="text" name="COMPANY_CITY" value="#company.getCity()#" /></p>
            <p>State: <input type="text" name="COMPANY_STATE" value="#company.getState()#" /></p>
            <p>Zip: <input type="text" name="COMPANY_ZIP" value="#company.getZip()#" /></p>
            <p>Main Phone: <input type="text" name="COMPANY_PHONE_MAIN" value="#company.getPhoneMain()#" /></p>

            <p>
                <cfif company.getCompanyID() eq 0>
                    <input type="submit" name="addCompany" value="Add Company" />
                <cfelse>
                    <input type="submit" name="editCompany" value="Edit Company" />
                    <input type="submit" name="deleteCompany" value="Delete Company" />
                </cfif>
                <input type="button" name="cancel" value="Cancel" onClick="history.back();" />
            </p>
        </form>
    </cfoutput>

Optionally, we can move the Javascript to an external file.

mach-ii-primer/js/05/companies.js

    function validateCompany()
    {
        var f = document.companyData;
        var err = "";

        if (f.COMPANY_NAME.value == "")
        {
            err += "Please enter a Company Name.\n";
        }
        if (f.COMPANY_ADDRESS_ONE.value == "")
        {
            err += "Please enter an Address.\n";
        }
        if (f.COMPANY_CITY.value == "")
        {
            err += "Please enter the City.\n";
        }
        if (f.COMPANY_STATE.value == "")
        {
            err += "Please enter the State.\n";
        }
        if (f.COMPANY_ZIP.value == "")
        {
            err += "Please enter the Zip Code.\n";
        }
        if (f.COMPANY_PHONE_MAIN.value == "")
        {
            err += "Please enter the Main Phone Number.\n";
        }

        if (err != "")
        {
            alert(err);
            return false;
        }

        return true;
    }

If we use an external JS file, then we need to reference it in company_form.cfm.

    <cfif event.getArg("jsFile") is not "">
        <script src="#event.getArg("jsFile")#" type="text/javascript"></script>
    </cfif>

The event-handler for showCompanyForm

    <!--
    mach-ii.05.xml
    -->
    <event-handler event="showCompanyForm" access="public">

    <event-arg name="pageTitle" value="Manage Company" />

        <!-- Optional external JS file -->
        <event-arg name="jsFile" value="/mach-ii-primer/m2/js/05/companies.js" />

        <notify listener="CompanyListener" method="getCompanyDetail" resultArg="companyBean" />

        <view-page name="header" />
        <view-page name="lhsMenu" contentArg="sidebar" />
        <view-page name="companyForm" contentArg="mainContent" />
        <view-page name="template" />
        <view-page name="footer" />

    </event-handler>

Filtering data

The showCompanyForm event will be the starting point for Creating, Updating and Deleting Company records.

The form is setup to show different submit actions based on url.COMPANY_ID.

    <cfif company.getCompanyID() eq 0>
        <input type="submit" name="addCompany" value="Add Company" />
    <cfelse>
        <input type="submit" name="editCompany" value="Edit Company" />
        <input type="submit" name="deleteCompany" value="Delete Company" />
    </cfif>

When you have multiple submit buttons in a form, whichever one you click will be the name/value pair that exists on the action page. (This is basic HTML functionality.)

Procedural version

In the procedural version, the form submits to a processing page.

    <form name="companyData"
        action="company_form_process.cfm"
        method="post"
        onSubmit="return validateCompany()">

A different query is run based on which submit button was used to submit the form.

mach-ii-primer/classic/company_form_process.cfm

    <cfif structKeyExists(form, "addCompany")>

        <cfquery name="qAddCompany" datasource="#request.DSN#">
            INSERT INTO
                CF_COMPANIES
            (
                COMPANY_NAME,
                COMPANY_ADDRESS_ONE,
                COMPANY_ADDRESS_TWO,
                COMPANY_CITY,
                COMPANY_STATE,
                COMPANY_ZIP,
                COMPANY_PHONE_MAIN
            )
            VALUES
            (
                '#form.COMPANY_NAME#',
                '#form.COMPANY_ADDRESS_ONE#',
                '#form.COMPANY_ADDRESS_TWO#',
                '#form.COMPANY_CITY#',
                '#form.COMPANY_STATE#',
                '#form.COMPANY_ZIP#',
                '#form.COMPANY_PHONE_MAIN#'
            )
        </cfquery>

    </cfif>

    <cfif structKeyExists(form, "editCompany")>

        <cfquery name="qEditCompany" datasource="#request.DSN#">
            UPDATE
                CF_COMPANIES
            SET
                COMPANY_NAME = '#form.COMPANY_NAME#',
                COMPANY_ADDRESS_ONE = '#form.COMPANY_ADDRESS_ONE#',
                COMPANY_ADDRESS_TWO = '#form.COMPANY_ADDRESS_TWO#',
                COMPANY_CITY = '#form.COMPANY_CITY#',
                COMPANY_STATE = '#form.COMPANY_STATE#',
                COMPANY_ZIP = '#form.COMPANY_ZIP#',
                COMPANY_PHONE_MAIN = '#form.COMPANY_PHONE_MAIN#'
            WHERE
                COMPANY_ID = #form.COMPANY_ID#
        </cfquery>

    </cfif>

    <cfif structKeyExists(form, "deleteCompany")>
        <cfquery name="qDeleteCompany" datasource="#request.DSN#">
            DELETE
            FROM
                CF_COMPANIES
            WHERE
                COMPANY_ID = #form.COMPANY_ID#
        </cfquery>
    </cfif>

    <cflocation URL="companies.cfm" />

Mach-II version

Now we're going to submit the form to an event instead of a page.

    <form name="companyData"
        action="index.cfm?event=processCompanyData"
        method="post"
        onSubmit="return validateCompany()">
    mach-ii.05.xml
    <event-handler event="processCompanyData" access="public">
        <filter name="FilterCompanyAction"/>
        <announce event="home" />
    </event-handler>

What is a Mach-II Event Filter?

Definition

An event filter is an object (component/CFC) that allows you to run some logic, then return true or false based on the results. Any filter that you create extends MachII.framework.EventFilter.

    <cfcomponent displayname="FilterCompanyAction"
            output="no"
            extends="MachII.framework.EventFilter"
            hint="Processes Company data based on the Submit button clicked.">

The required function filterEvent takes 3 arguments.

filterEvent()

    <cffunction name="filterEvent" access="public" output="false" returntype="boolean">
        <cfargument name="event" type="MachII.framework.Event" required="yes" />
        <cfargument name="eventContext" type="MachII.framework.EventContext" required="yes" />
        <cfargument name="paramArgs" type="struct" required="yes" />
    </cffunction>

You can optionally create a configure() method to handle anything that needs to run when the Filter is created. This works the same as a Listerner's configure() method.

All Filters are created when the application loads for the first time. They are placed into the application scope, so be aware of your variable scopes.

Usage

When a filter returns true, then Mach-II continues processing the event-handler. In the event-handler processCompanyData, if the filter FilterCompanyAction returns true, then the announce command is run and we proceed to the "home" event.

FilterCompanyAction returns true

    <filter name="FilterCompanyAction"/>
    <announce event="home" />

When a filter returns false, Mach-II stops processing the rest of the current event-handler and goes to the next one. Usually you'll announce the next event before returning false. (mach-ii-primer/m2/filters/05/FilterCompanyAction.cfc)

    <cfcomponent displayname="FilterCompanyAction"
        output="no"
        extends="MachII.framework.EventFilter"
        hint="Processes Company data based on the Submit button clicked.">

        <cffunction name="filterEvent" access="public" output="false" returntype="boolean">
            <cfargument name="event" type="MachII.framework.Event" required="yes" />
            <cfargument name="eventContext" type="MachII.framework.EventContext" required="yes" />
            <cfargument name="paramArgs" type="struct" required="yes" />

            <cfif event.getArg("addCompany") is not "">
                <cfset announceEvent("processCompanyCreate", arguments.event.getArgs()) />
                <cfreturn false />
            </cfif>

            <cfif event.getArg("editCompany") is not "">
                <cfset announceEvent("processCompanyUpdate", arguments.event.getArgs()) />
                <cfreturn false />
            </cfif>

            <cfif event.getArg("deleteCompany") is not "">
                <cfset announceEvent("processCompanyDelete", arguments.event.getArgs()) />
                <cfreturn false />
            </cfif>

            <cfreturn true />

        </cffunction>

    </cfcomponent>

In the above code, if the "addCompany" submit button was clicked, then event.getArg("addCompany") will not be an empty string, so the Filter then announces the event processCompanyCreate and copies all of the current event Args to that event. Then it returns false, telling Mach-II to stop processing the current event-handler (processCompanyData).

So far, we have replaced part of the functionality of the procedural file "company_form_process.cfm". Now we just need to handle the database interactions.

Chaining Events

So we start off at showCompanyForm.

    <event-handler event="showCompanyForm" access="public">
        <event-arg name="pageTitle" value="Manage Company" />
        <notify listener="CompanyListener" method="getCompanyDetail" resultArg="companyBean" />

        <view-page name="header" />
        <view-page name="lhsMenu" contentArg="sidebar" />
        <view-page name="companyForm" contentArg="mainContent" />
        <view-page name="template" />
        <view-page name="footer" />
    </event-handler>

The form in that event submits to the event processCompanyData.

    <event-handler event="processCompanyData" access="public">
        <filter name="FilterCompanyAction"/>
        <announce event="home" />
    </event-handler>

Based on the action (button) we chose in showCompanyForm, the filter FilterCompanyAction then announces the next event.

If we chose to CREATE a Company

    <event-handler event="processCompanyCreate" access="private">
        <notify listener="CompanyListener" method="createCompany" />
        <announce event="showCompanies" />
    </event-handler>

The event processCompanyCreate notifies the Listener CompanyListener to call the method createCompany.

The method createCompany creates an instance of the CompanyDAO, then creates and populates an instance of the Company bean with the form data and finally passes the Company bean to the create() method of the DAO. (mach-ii-primer/m2/model/companies/05/CompanyListener.cfc - New function: createCompany())

    <cffunction name="createCompany"
        access="public"
        output="false"
        returntype="boolean"
        hint="Creates a new Company record.">

        <cfargument name="event" type="MachII.framework.Event" required="true" />

        <cfset var companyDAO = createObject("component", "CompanyDAO").init( DSN = request.DSN ) />
        <cfset var company = createObject("component", "Company").init(
            CompanyID = arguments.event.getArg("COMPANY_ID"),
            Name = arguments.event.getArg("COMPANY_NAME"),
            AddressOne = arguments.event.getArg("COMPANY_ADDRESS_ONE"),
            AddressTwo = arguments.event.getArg("COMPANY_ADDRESS_TWO"),
            City = arguments.event.getArg("COMPANY_CITY"),
            State = arguments.event.getArg("COMPANY_STATE"),
            Zip = arguments.event.getArg("COMPANY_ZIP"),
            PhoneMain = arguments.event.getArg("COMPANY_PHONE_MAIN")
        ) />

        <cfreturn companyDAO.create( company ) />
    </cffunction>

mach-ii-primer/m2/model/companies/05/CompanyDAO.cfc - New function: create()

    <cffunction name="create"
        access="public"
        returntype="boolean"
        output="false"
        hint="Create a new Company record.">

        <cfargument name="company" type="Company" required="true" hint="Company bean" />

        <cfset var qAddCompany = "" />

        <cftransaction>
            <cftry>
                <cfquery name="qAddCompany" datasource="#variables.DSN#">
                    INSERT INTO
                    CF_COMPANIES
                    (
                    COMPANY_NAME,
                    COMPANY_ADDRESS_ONE,
                    COMPANY_ADDRESS_TWO,
                    COMPANY_CITY,
                    COMPANY_STATE,
                    COMPANY_ZIP,
                    COMPANY_PHONE_MAIN
                    )
                    VALUES
                    (
                    <cfqueryparam value="#arguments.company.getName()#" cfsqltype="cf_sql_varchar" />,
                    <cfqueryparam value="#arguments.company.getAddressOne()#" cfsqltype="cf_sql_varchar" />,
                    <cfqueryparam value="#arguments.company.getAddressTwo()#" cfsqltype="cf_sql_varchar" />,
                    <cfqueryparam value="#arguments.company.getCity()#" cfsqltype="cf_sql_varchar" />,
                    <cfqueryparam value="#arguments.company.getState()#" cfsqltype="cf_sql_varchar" />,
                    <cfqueryparam value="#arguments.company.getZip()#" cfsqltype="cf_sql_varchar" />,
                    <cfqueryparam value="#arguments.company.getPhoneMain()#" cfsqltype="cf_sql_varchar" />
                    )
                </cfquery>

                <cfcatch type="database">
                    <cftransaction action="rollback" />
                    <cfreturn false />
                </cfcatch>

            </cftry>
        </cftransaction>

        <cfreturn true />
    </cffunction>

Once all of this has processed, the event showCompanies is loaded.

If we chose to UPDATE a Company

    <event-handler event="processCompanyUpdate" access="private">
        <notify listener="CompanyListener" method="updateCompany" />
        <announce event="showCompanies" />
    </event-handler>

The event processCompanyUpdate notifies the Listener CompanyListener to call the method updateCompany.

The method updateCompany creates an instance of the CompanyDAO, then creates and populates an instance of the Company bean with the form data and finally passes the Company bean to the update() method of the DAO.

mach-ii-primer/m2/model/companies/05/CompanyListener.cfc - New function: updateCompany()

    <cffunction name="updateCompany"
        access="public"
        output="false"
        returntype="boolean"
        hint="Updates a Company record.">

        <cfargument name="event" type="MachII.framework.Event" required="true" />

        <cfset var companyDAO = createObject("component", "CompanyDAO").init( DSN = request.DSN ) />
        <cfset var company = createObject("component", "Company").init(
            CompanyID = arguments.event.getArg("COMPANY_ID"),
            Name = arguments.event.getArg("COMPANY_NAME"),
            AddressOne = arguments.event.getArg("COMPANY_ADDRESS_ONE"),
            AddressTwo = arguments.event.getArg("COMPANY_ADDRESS_TWO"),
            City = arguments.event.getArg("COMPANY_CITY"),
            State = arguments.event.getArg("COMPANY_STATE"),
            Zip = arguments.event.getArg("COMPANY_ZIP"),
            PhoneMain = arguments.event.getArg("COMPANY_PHONE_MAIN")
        ) />

        <cfreturn companyDAO.update( company ) />

    </cffunction>

mach-ii-primer/m2/model/companies/05/CompanyDAO.cfc - New function: update()

    <cffunction name="update"
        access="public"
        returntype="boolean"
        output="false"
        hint="Updates a Company record.">

        <cfargument name="company" type="Company" required="true" hint="Company bean" />

        <cfset var qEditCompany = "" />
        <cftransaction>
            <cftry>
                <cfquery name="qEditCompany" datasource="#variables.DSN#">
                    UPDATE
                    CF_COMPANIES
                    SET
                    COMPANY_NAME = <cfqueryparam value="#arguments.company.getName()#" cfsqltype="cf_sql_varchar" />,
                    COMPANY_ADDRESS_ONE = <cfqueryparam value="#arguments.company.getAddressOne()#" cfsqltype="cf_sql_varchar" />,
                    COMPANY_ADDRESS_TWO = <cfqueryparam value="#arguments.company.getAddressTwo()#" cfsqltype="cf_sql_varchar" />,
                    COMPANY_CITY = <cfqueryparam value="#arguments.company.getCity()#" cfsqltype="cf_sql_varchar" />,
                    COMPANY_STATE = <cfqueryparam value="#arguments.company.getState()#" cfsqltype="cf_sql_varchar" />,
                    COMPANY_ZIP = <cfqueryparam value="#arguments.company.getZip()#" cfsqltype="cf_sql_varchar" />,,
                    COMPANY_PHONE_MAIN = <cfqueryparam value="#arguments.company.getPhoneMain()#" cfsqltype="cf_sql_varchar" />
                    WHERE
                    COMPANY_ID = <cfqueryparam value="#arguments.company.getCompanyID()#" cfsqltype="cf_sql_integer" />
                </cfquery>

                <cfcatch type="database">
                    <cftransaction action="rollback" />
                    <cfreturn false />
                </cfcatch>
            </cftry>
        </cftransaction>
        <cfreturn true />
    </cffunction>

Once all of this has processed, the event showCompanies is loaded.

If we chose to DELETE a Company

    <event-handler event="processCompanyDelete" access="private">
        <notify listener="CompanyListener" method="deleteCompany" />
        <announce event="showCompanies" />
    </event-handler>

The event processCompanyDelete notifies the Listener CompanyListener to call the method deleteCompany.

The method deleteCompany creates an instance of the CompanyDAO, then creates and populates an instance of the Company bean with the COMPANY_ID only and finally passes the Company bean to the delete() method of the DAO.

mach-ii-primer/m2/model/companies/05/CompanyListener.cfc - New function: deleteCompany()

    <cffunction name="deleteCompany"
        access="public"
        output="false"
        returntype="boolean"
        hint="Updates a Company record.">

        <cfargument name="event" type="MachII.framework.Event" required="true" />

        <cfset var companyDAO = createObject("component", "CompanyDAO").init( DSN = request.DSN ) />
        <cfset var company = createObject("component", "Company").init(
            CompanyID = arguments.event.getArg("COMPANY_ID")
        ) />

        <cfreturn companyDAO.delete( company ) />
    </cffunction>

mach-ii-primer/m2/model/companies/05/CompanyDAO.cfc - New function: delete()

    <cffunction name="delete"
        access="public"
        returntype="boolean"
        output="false"
        hint="Updates a Company record.">

        <cfargument name="company" type="Company" required="true" hint="Company bean" />

        <cfset var qDelCompany = "" />

        <cftransaction>
            <cftry>
                <cfquery name="qDelCompany" datasource="#variables.DSN#">
                    DELETE
                    FROM
                    CF_COMPANIES
                    WHERE
                    COMPANY_ID = <cfqueryparam
                                    value="#arguments.company.getCompanyID()#"
                                    cfsqltype="cf_sql_integer"
                                    />
                </cfquery>
                <cfcatch type="database">
                    <cftransaction action="rollback" />
                    <cfreturn false />
                </cfcatch>
            </cftry>
        </cftransaction>

        <cfreturn true />
    </cffunction>

Once all of this has processed, the event showCompanies is loaded.

Ack! Carpel Tunnel!

Yes, that's a heck of a lot of code for such a small application. If this was a real world app, I'm sure I'd just create it the procedural way. However, the concepts and techniques shown so far are the building blocks used in many high traffic and mission critical applications.

You can see these changes in action by using the version 5 files.

  1. Rename the version 4 mach-ii.xml file to mach-ii.04.xml
  2. Rename mach-ii.05.xml to mach-ii.xml
  3. Reload the application and click the Companies link in the left-hand navigation.

Other Chapters

The Mach-II Primer has now caught up with the Object Oriented ColdFusion Primer. I'm working on a post for that primer which will cover a Complex DAO (working with multiple tables or data sources). After that, I'll get into the Service Object and Object Factory. Once those are done, I'll come back to the Mach-II Primer to show how to implement those objects in the sample application.

But before then: We've been converting this application to an MVC framework in an effort to reduce the amount of redundant code. You should have noticed by now that there are a lot of the same objects being created in the Listeners. They're being created every time a function is called. That's redundant code too.

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

Clone this wiki locally