Mach-ii for newbies - part1 Getting started
Mach-ii for newbies - part1
Getting started
Written by Trond Ulseth

1. Introduction

Back in the good old days of my programming career, about 5 years ago, life was easy. Coming from a html/designer background (I actually started my learning with a web site on GeoCities and a copy of Paint Shop Pro) the path to learning ColdFusion was pretty easy going. The amount of online tutorials and paperback books being eager to teach me this fantastic way of programming my homepages almost moved me to my tears. It was just days before I had my own ColdFusion driven guestbook up and running.

I’ve been working as a web developer for about 4 years now, with more spaghetti code on my consciousness than any sane mind can bear to think about. But with ColdFusion MX came cfc’s, and even though it took a while before I discovered them, thanks to them I now find my code extremely tidier and easier to maintain, and I must say that to some degree my style of coding has evolved into some kind of framework which is pretty neat.

But, and there’s a big but, I have come to realize that there are far better ways of organizing code than I can ever hope of mixing together by my self. And after a lot of sniffing around (Google and Fullasagoog) and chatting for advice on IRC, I decided that I would dip my toes into the waters of the mach-ii framework (http://www.mach-ii.com).

However, being spoiled with mountains of books and terabytes of tutorials, I found the available documentation for mach-ii disappointingly lacking. This is not to say that there’s no documentation at all, but what I found I did not find very pedagogical for a total newbie to it. So after months of not quite getting started I decided. I will just dive into it and make a simple application, my good old guestbook, and write a tutorial as I go along. And hopefully other newbie’s, like me, will find this useful as well.

I must underline, I am learning as I’m writing, or writing as I’m learning. I can therefore not give any guaranty that what I am doing is totally best practices, or that I have so much understanding of what I’m doing that I explain it as clear as could be possible. I’ll try though to write in an easy to understand and slightly entertaining way.

I’m going to presume that you have a pretty good grasp of ColdFusion from before. I’m not going to explain ColdFusion tags and functions, variables and properties, what cfc’s are etc. This stuff is well covered from before in the before mentioned amount of books and existing tutorials.

2. Getting prepared

First go to the mach-ii code page (http://www.mach-ii.com/code.cfm) and download the latest version of the Framework Code (version 1.0.10 as I’m writing), and the Application Code Skeleton. Extract them into the home directory of your web server. On my machine they are placed like this:

C:\Inetpub\wwwroot\MachII
C:\Inetpub\wwwroot\MachAppSkeleton

This placement is what I’ll assume during the tutorial. It should not be hard to configure it to work in another set up, but I’m not going to cover that here (maybe in a later tutorial when I have to do it my self).

Now rename the MachAppSkeleton directory to MyGuestbook.

Open the Application.cfm file in the newly renamed MyGuestbook folder, and edit the name attribute of the cfapplication tag to “My Guestbook”.

<cfapplication name="My Guestbook" sessionmanagement="yes" />

Open the mach-ii.xml file in the config directory under MyGuestbook. Under properties change the application root value to /MyGuestbook

<properties>
   <property name="applicationRoot" value="/MyGuestbook" />
   <property name="defaultEvent" value="DEFAULT_EVENT" />
   <property name="eventParameter" value="event" />
   <property name="parameterPrecedence" value="form" />
   <property name="maxEvents" value="10" />
   <property name="exceptionEvent" value="exceptionEvent" />
</properties>

(There’s a closer explanation of these properties at the end of chapter 3)

Now if you open http://localhost/MyGuestbook you should get a blank page, with no errors.

However, blank pages can be rather boring after a while, and can under no circumstance be called a guestbook, so even if you hear the call of the sofa, you need to stay with me a while longer.

Everything in mach-ii is controlled by events. Or at least so I think. It might be that later on in the process we might find this to be wrong. But for now I will pretend it is so. So in order to show some content we need to specify the defaultEvent in the mach-ii.xml file (the mach-ii.xml file is pretty much the heart of the mach-ii framework – so don’t bother closing it. We will do pretty some stuff in here). Let us set the default event value to “showMain”.

<properties>
   <property name="applicationRoot" value="/MyGuestbook" />
   <property name="defaultEvent" value="showMain" />
   <property name="eventParameter" value="event" />
   <property name="parameterPrecedence" value="form" />
   <property name="maxEvents" value="10" />
   <property name="exceptionEvent" value="exceptionEvent" />
</properties>

Now we need to specify what the showMain event will do. Further down the mach-ii.xml file you’ll find the event handlers:

<!-- EVENT-HANDLERS -->
<event-handlers>
   <event-handler event=
"DEFAULT_EVENT" access="public">
   
<!-- any legal elements -->
</event-handler>

Let’s make some changes. We change the event to showMain (because that is the event we declared as default event just now) and put in an element called view-page:

<!-- EVENT-HANDLERS -->
<event-handlers>
   <event-handler event=
"showMain" access="public">
   <view-page name=
"mainTemplate" />
</event-handler>

As you probably can guess (at least I did, and I’m not among the sharpest knifes in the drawer), view-page tell the application what should be shown in the browser window.

There’s one little thing more we have to do in the mach-ii.xml file (I told you we were going to do pretty some stuff in here). Let’s go even further down to find the page views:

<!-- PAGE-VIEWS -->
<page-views>
   <page-view name=
"NEW_VIEW_NAME" page="/views/NEW_VIEW_FILE.cfm" />
   <page-view name=
"exception" page="/views/exception.cfm" />
</page-views>

Now let’s edit the name of the first page view to reflect the view we called from our showMain event, and make it call something more intuitive than NEW_VIEW_FILE.cfm:

<!-- PAGE-VIEWS -->
<page-views>
   <page-view name=
"mainTemplate" page="/views/mainTemplate.cfm" />
   <page-view name=
"exception" page="/views/exception.cfm" />
</page-views>

(We see that there’s another page view called exception there – let’s not bother about that. It’s probably there for a reason, and maybe we’ll stumble across it later)

Now the last thing we need to do in order to get something on the screen is to create the mainTemplate.cfm file and put it in the views folder. For now let’s make it simple:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>
My Guestbook</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>

<body>
<h2>
Welcome to my Guestbook!</h2>
</body>
</html>

Now let’s open http://localhost/MyGuestbook and see if there’s some change. Voila! Not exactly rocket science though. Actually we would have accomplished the same just with the mainTemplate.cfm file. Oh well – we’ll just have to trust that there will be some payoff for the extra work further down the road.

Now let’s try to put some meat on the skeleton.

3. Getting started with mach-ii development

Now it’s time to start shuffling some data into our application. The first thing we’ll do is to give visitors the opportunity to add their messages to the guestbook.

Bellow the header in mainTemplate.cfm adds the following basic form:

<form method="post" name="msgForm">
   <fieldset>
      <legend>
Add your message</legend>
      Name:<br>
      <input name="name" type="text"><br>
      Email:<br>
      <input name="email" type="text"><br>
      Message:<br>
      <textarea name="message" cols="40" rows="5" wrap="virtual"></textarea><br>
      <input type="submit" name="Submit" value="Post message">
   </fieldset>
</form>

Before moving on to how we can make mach-ii aware of the data that is entered (notice we did not add any action attribute to the form yet), we need to have somewhere to store the data. In our case we’ll chose a database (an xml file would be another good option).

In my case I’ll make a MyGuestbook database in MySQL, for now with the following single table.

guestbook
id int(11) auto_increment, primary key
date datetime
name varchar(50)
email varchar(50)
message longtext

Here’s a table creation script:

CREATE TABLE 'guestbook' (
      'id' int(11) NOT NULL auto_increment,
      'date' datetime NOT NULL default '0000-00-00 00:00:00',
      'name' varchar(50) NOT NULL default '',
      'email' varchar(50) NOT NULL default '',
      'message' longtext NOT NULL,
      PRIMARY KEY ('id')
) TYPE=MyISAM AUTO_INCREMENT=1 ;

 

Have you created the database? Ok – then you must define the data source in the ColdFusion administrator. Let’s call it…. MyGuestbook (ok, I’m predictable here. I’ll let the mach-ii framework itself provide the excitement).

If you’re anything like me you are probably used to defining your dsn in the application.cfm file (something like <cfset db = “MyGuestbook”>). Not so in mach-ii. Here we’ll go back to the trusted old mach-ii.xml file (I think we have come pretty some way when we can refer to it as the trusted old...). Up in the properties section well add a property:

<properties>
    <property name=
"applicationRoot" value="/MyGuestbook" />
    <property name=
"defaultEvent" value="showMain" />
    <property name=
"eventParameter" value="event" />
    <property name=
"parameterPrecedence" value="form" />
    <property name=
"maxEvents" value="10" />
    <property name=
"exceptionEvent" value="exceptionEvent" />
    <!-- Application variables -->
    <property name=
"dns" value="MyGuestbook" />
</properties>

Now we’re ready to maneuver submitted messages into that database. We’ll start with the beginning. We need an action parameter for that form. Remember earlier I said that I think everything in mach-ii is controlled by events? We’ll remember that as we set the action parameter like this:

<form action="index.cfm?event=message.create" method="post" name="msgForm">

Also remember that events had associated event handlers. We now need to add an event handler for the message.create event.

<event-handler event="message.create" access="public">
    <event-bean name="message" type="MyGuestbook.model.message.message" />
    <notify listener="messageListener" method="createMessage" />
</event-handler>

Whoa – what’s happening here? In the event handler we worked with earlier we had a view-page method. That one we could understand without invoking too many of the little grey ones. Now we introduce two new ones which by first look are not that self explanatory. We are heading for slightly deeper waters.

The bean

The first new method in the event handler is “event-bean”. A bean is a cfc which models a single instance of an object. In our case the bean creates a message object from the info passed from the form. A bean consist of an init() method as well as so called getters and setters for each of the object attributes.

Let’s take a look at the code for our bean before I try to explain anything else (key word being try).

<cfcomponent displayname="message" hint="I model a single message">

    <cffunction name="init" access="public" returntype="message" output="false" displayname="Message Constructor" hint="I initialize a message.">
            <cfargument name="id" type="numeric" required="false" default="0" displayname="" hint="" />
            <cfargument name="date" type="date" required="false" default="#Now()#" displayname="" hint="" />
            <cfargument name="name" type="string" required="false" default="" displayname="" hint="" />
            <cfargument name="email" type="string" required="false" default="" displayname="" hint="" />
            <cfargument name="message" type="string" required="false" default="" displayname="" hint="" />

            <cfscript>
                variables.instance = structNew();
                setID(arguments.id);
                setDate(arguments.date);
                setName(arguments.name);
                setEmail(arguments.email);
                setMessage(arguments.message);

            </cfscript>

            <cfreturn this />
    </cffunction>

    <!--- GETTERS/SETTERS --->

    <cffunction name="getID" access="package" returntype="numeric" output="false" displayname="" hint="I return ID">
        <cfreturn variables.instance.id />
    </cffunction>

    <cffunction name="setID" access="package" returntype="void" output="false" displayname="" hint="I set ID">
        <cfargument name="id" type="numeric" required="true" />
        <cfset variables.instance.id = arguments.id />
    </cffunction>

    <cffunction name="getDate" access="package" returntype="date" output="false" displayname="" hint="I return date">
        <cfreturn variables.instance.date />
    </cffunction>

    <cffunction name="setDate" access="package" returntype="void" output="false" displayname="" hint="I set date">
        <cfargument name="date" type="date" required="true" />
        <cfset variables.instance.date = arguments.date />
    </cffunction>

    <cffunction name="getName" access="package" returntype="string" output="false" displayname="" hint="I return name">
        <cfreturn variables.instance.name />
    </cffunction>

    <cffunction name="setName" access="package" returntype="void" output="false" displayname="" hint="I set name">
        <cfargument name="name" type="string" required="true" />
        <cfset variables.instance.name = arguments.name />
    </cffunction>

    <cffunction name="getEmail" access="package" returntype="string" output="false" displayname="" hint="I return email">
        <cfreturn variables.instance.email />
    </cffunction>

    <cffunction name="setEmail" access="package" returntype="void" output="false" displayname="" hint="I set email">
        <cfargument name="email" type="string" required="true" />
        <cfset variables.instance.email = arguments.email />
    </cffunction>

    <cffunction name="getMessage" access="package" returntype="string" output="false" displayname="" hint="I return message">
        <cfreturn variables.instance.message />
    </cffunction>

    <cffunction name="setMessage" access="package" returntype="void" output="false" displayname="" hint="I set message">
        <cfargument name="message" type="string" required="true" />
        <cfset variables.instance.message = arguments.message />
    </cffunction>


</cfcomponent>

Save this bean as c:\\inetpub\wwwroot\MyGuestbook\model\message\message.cfc
Now remember how we called the bean:

<event-bean name="message" type="MyGuestbook.model.message.message" />

The type attribute is actually the path to the cfc file, under the web root, using dot notation.

Let’s try to understand what happens inside this bean. When we use the “event-bean” method, mach-ii passes in any available form and url parameters as arguments, and call the init() method.

The init() method in turn call the internal setter methods to populate it self with the arguments passed in from the form and/or the url.

So in our case we now have an instance of the message object with the values from the form.

The listener

First a listener need to be defined in the mach-ii.xml file (you did not close it, did you?).
If we look right bellow where the properties are defined you’ll find this:

<!-- LISTENERS -->
<listeners>
    <!--
        <listener name="yourListenerName" type="fullyQualifiedDotDelimitedPathToCFC">
            <invoker type=
"MachII.framework.invokers.CFCInvoker_Event" />
            <parameters>
                <parameter name=
"yourParameterName" value="yourParameterValue" />
            </parameters>
        </listener>
    -->
</listeners>

Here there’s a commented out example of how a listener is defined. To make it work for us we need to change a couple of things:

<!-- LISTENERS -->
<listeners>
    <listener name=
"messageListener" type="MyGuestbook.model.message.messageListener">
        <invoker type=
"MachII.framework.invokers.CFCInvoker_Event" />
    </listener>
</listeners>

What we’ve done here is to give the listener the appropriate name, and given it a type, which just like with the type of the event bean is a dot notated path to a listener cfc file.

We kept the invoker type argument with the default value. And then we remove the parameters. This might seem a bit strange, but as we will soon see the mach-ii framework will provide the listener with the bean we just initiated, and the bean contains all parameters we need for now.

Time to look at the messageListener.cfc code:

<cfcomponent extends="MachII.framework.Listener" displayname="Message Listener" hint="I am the listener for messages">

    <cffunction name="configure" access="public" returntype="void" output="true"
                      displayname="Listener Constructor" hint="I initialize this listener as part of the framework startup.">

        <cfscript>
            var dsn = getAppManager().getPropertyManager().getProperty("dsn");
            variables.messageCRUD = CreateObject(
"component", "MyGuestbook.model.message.messageCRUD").init(dsn, "MyGuestbook");
        </cfscript>
    </cffunction>

    <cffunction name="createMessage" access="public" returntype="void" output="false"
                     displayname="Create Message" hint="I cause message to be created from the current event object.">

            <cfargument name="event" type="MachII.framework.Event" required="yes" displayname="Event" hint="I am the current event" />
            <cfset var message = arguments.event.getArg("message") />
            <cfset variables.messageCRUD.create(message) />
    </cffunction>

</cfcomponent>

The configure method of listeners are initialized when the framework start up. In our case as soon as we open http://localhost/MyGuestbook. In rough words, what happens in our configure method here is that we "fish" out the data source we defined as a property in the mach-ii.xml file, and feed it to a CRUD component (what a CRUD component is we’ll get to in a minute).

Over to the createMessage method. We see from the argument named event, that the mach-ii framework provides the current bean as an argument collection. We create a var called message which to pass the arguments over to the create method of the messageCRUD object that was created in the configure method.

And finally the CRUD

If you found the concept of beans and listeners a bit new and challenging (I know I did) it might be a bit of a release to come to the CRUD component. A CRUD component deals with the communication to the database, and therefore mainly consists of familiar SQL queries. CRUD components don’t handle all communication with the database though. They only deal with one record at a time, and use the four main SQL methods for doing so; INSERT, SELECT, UPDATE and DELETE. The CRUD components get their name from the four methods used to call these four SQL methods; Create, Read, Update and Delete.

For now, our CRUD component will just have the first method, Create (it also have an init method on the top, which we remember was called from the configure method of the listener above, to get in the datasource).

<cfcomponent displayname="" hint="I abstract data access for messages">

    <cffunction name="init" access="public" returntype="MyGuestbook.model.message.messageCRUD" output="false" >
        <cfargument name="dsn" type="string" required="true" />
        <cfset variables.dsn = arguments.dsn />
        <cfreturn this />
    </cffunction>

    <cffunction name="create" returntype="void" output="false" hint="CRUD method">
        <cfargument name="message" type="MyGuestbook.model.message.message" required="yes" displayname="create"
                             hint="I am the message from which to create a record" />
        <cfset var messageInsert = 0 />
        <cfquery name="messageInsert" datasource="#variables.dsn#" >
            INSERT INTO guestbook (
                                        date,
                                        name,
                                        email,
                                        message
                                 )
                                 VALUES (
                                        #arguments.message.getDate()#,
                                        '#trim(arguments.message.getName())#',
                                        '#trim(arguments.message.getEmail())#',
                                        '#trim(arguments.message.getMessage())#',
                                 )
        </cfquery>

        <cfreturn />
    </cffunction>

</cfcomponent>

Now, finally, open http://localhost/MyGuestbook, fill in and submit the form. If your code looks like mine you should now get a blank page. And if you check your database you’ll see that the information from the form is safely saved (until we later on will learn how to delete database records in mach-ii that is).

Now are you ready for an assignment? Instead of a blank page we want to show the same page we came from. I’m pretty sure you can figure out how.

The six default properties

Remember one of the very first things we did was changing some of the six default properties in mach-ii? Before we end the first part of this tutorial series we’ll take a look at all six of them and what they are doing.

Here’s a repetition of how they look (after we changed a couple of them):

<property name="applicationRoot" value="/MyGuestbook" />
<property name="defaultEvent" value="showMain" />
<property name="eventParameter" value="event" />
<property name="parameterPrecedence" value="form" />
<property name="maxEvents" value="10" />
<property name="exceptionEvent" value="exceptionEvent" />

applicationRoot:
This is the path to the application relative to the web root

defaultEvent:
Remember we said that everything in mach-ii is controlled by events? Events are declared either by url or form variables. But what if there is no url or form variable present? What should mach-ii do then? The answer to that question should be answered by defaultEvent.

eventParameter:
Now we just learned that events are triggered by url or form variables. But what url or form variable should it be. If all url and form variables were to trigger events that would be total anarchy. So the eventParameter decides what the name of the triggering variable should be. By default this is “event”. Further up we had a form started like this:
<form action="index.cfm?event=message.create" method="post" name="msgForm">
If we change the eventParameter to for example “go” the form would have to start like this:
<form action="index.cfm?go=message.create" method="post" name="msgForm">

parameterPrecedence:
So events can be triggered by both url and form eventParameters. What if for some reason the eventParameter is present? Or any other duplicate parameter for that sake. With parameterPrecedence we set which scope should have precedence.

maxEvents:
One thing we’ve not talked about yet is that events in turn can announce other events. In some situations this could lead to recursive event calls.

exceptionEvent:
The exception event is called anytime there is an error within the framework that isn’t handled in your code (you do use error trapping don’t you?).

All ColdFusion Tutorials By Author: Trond Ulseth
  • Mach-ii for newbies - part1 Getting started
    The first in a series of tutorials explaining how to develop applications with the mach-ii framework. Written by a mach-ii newbie himself, this tutorials take on kind of a collaborative learning aproach.
    Author: Trond Ulseth
    Views: 56,001
    Posted Date: Saturday, January 15, 2005
  • Mach-ii for newbies - part 2
    Continuing where part 1 left of - this one deals with reading, updating and deleting database records the mach-ii way.
    Author: Trond Ulseth
    Views: 26,720
    Posted Date: Thursday, March 31, 2005
Download the EasyCFM.COM Browser Toolbar!