Ok, so we've got the concept of a DAO and how it interacts with a bean. Here's an example of a form and an action page using Object Oriented Coldfusion.

A Form and an Action page

Let's say you've got a page with a list of Contacts, each item has a link to contact_form.cfm with the CONTACT_ID in the query string.

contact_list.cfm
view plain print about
1<cfset contactList = application.ContactGateway.getContacts() />
2
3<ul>
4<cfoutput query="contactList">
5    <li>
6        <a href="contact_form.cfm?CONTACT_ID=#contactList.CONTACT_ID#">
7            #contactList.LAST_NAME#, #contactList.FIRST_NAME#
8        </a>
9    </li>
10</cfoutput>
11</ul>

output

Updating a record

This form is going to update an existing record.

contact_form.cfm?CONTACT_ID=3
view plain print about
1<cfparam name="url.CONTACT_ID" type="numeric" default="0" />
2
3<!--- List of Categories --->
4<cfset categories = application.categoryGateway.getCategories() />
5<!--- Instance of a Contact Bean --->
6<cfset contact = createObject("component", "Contact").init( CONTACT_ID = url.CONTACT_ID ) />
7<!--- Populate Contact Bean --->
8<cfset application.contactDAO.read( contact ) />
9
10<cfoutput>
11<form name="contact_data" action="contact_update.cfm" method="post" onsubmit="return false;">
12
13    <input type="hidden" name="CONTACT_ID" value="#contact.getContactID()#" />
14
15    <p>Category: <select name="CATEGORY_ID">
16        <option value="0">-- Select --</option>
17        <cfloop query="categories">
18            <cfif contact.getCategoryID() eq categories.CATEGORY_ID>
19                <option value="#categories.CATEGORY_ID#" selected="selected">#categories.CATEGORY_LABEL#</option>
20            <cfelse>
21                <option value="#categories.CATEGORY_ID#">#categories.CATEGORY_LABEL#</option>
22            </cfif>
23        </cfloop>
24    </select></p>
25
26    <p>First Name: <input type="text" name="FIRST_NAME" value="#contact.getFirstName()#" /></p>
27
28    <p>Last Name: <input type="text" name="LAST_NAME" value="#contact.getLastName()#" /></p>
29
30    <p><input type="submit" name="processContact" /></p>
31
32</form>
33</cfoutput>

output

Category:

First Name:

Last Name:

As for the processing page to update this Contact record, you have a few options:

contact_update.cfm 1. Passing ordered argument values:
view plain print about
1<cfif structKeyExists(form, "processContact")>
2    
3    <cfset contact = createObject("component", "Contact").init(
4            form.CONTACT_ID,
5            form.CATEGORY_ID,
6            form.FIRST_NAME,
7            form.LAST_NAME
8            ) /
>

9
10    <cfset application.contactDAO.update( contact ) />
11            
12    <cflocation url="contact_list.cfm" addtoken="false" />
13    
14</cfif>

2a. Passing name / value pairs:

view plain print about
1<cfif structKeyExists(form, "processContact")>
2    
3    <cfset contact = createObject("component", "Contact").init(
4            CONTACT_ID = form.CONTACT_ID,
5            CATEGORY_ID = form.CATEGORY_ID,
6            FIRST_NAME = form.FIRST_NAME,
7            LAST_NAME = form.LAST_NAME
8            ) /
>

9
10    <cfset application.contactDAO.update( contact ) />
11            
12    <cflocation url="contact_list.cfm" addtoken="false" />
13    
14</cfif>

2b. Passing name / value pairs using cfinvoke:

view plain print about
1<cfif structKeyExists(form, "processContact")>
2
3    <cfinvoke
4        component="Contact"
5        method="init">

6        
7        <cfinvokeargument name="CONTACT_ID"
8            value="#form.CONTACT_ID#" />

9        <cfinvokeargument name="CATEGORY_ID"
10            value="#form.CATEGORY_ID#" />

11        <cfinvokeargument name="FIRST_NAME"
12            value="#form.FIRST_NAME#" />

13        <cfinvokeargument name="LAST_NAME"
14            value="#form.LAST_NAME#" />

15            
16    </cfinvoke>
17    
18    <cfinvoke
19        component="#application.ContactDAO#"
20        method="update">

21        
22        <cfinvokeargument name="Contact"
23            value="#contact#" />

24            
25    </cfinvoke>
26        
27    <cflocation url="contact_list.cfm" addtoken="false" />
28    
29</cfif>

3a. Since the form field names match the argument names of the Update() method, you can pass the form scope as an Argument Collection:

view plain print about
1<cfif structKeyExists(form, "processContact")>
2
3    <cfset contact = createObject("component", "Contact").init(
4            argumentCollection = form
5            ) /
>

6
7    <cfset application.contactDAO.update( contact ) />
8            
9    <cflocation url="contact_list.cfm" addtoken="false" />
10    
11</cfif>

3b. Passing an Argument Collection using cfinvoke:

view plain print about
1<cfif structKeyExists(form, "processContact")>
2    
3    <cfinvoke
4        component="Contact"
5        method="init"
6        argumentCollection="form" />

7
8    <cfinvoke
9        component="#application.ContactDAO#"
10        method="update" />

11        
12         <cfinvokeargument name="Contact"
13            value="#contact#" />

14            
15    </cfinvoke>
16        
17    <cflocation url="contact_list.cfm" addtoken="false" />
18    
19</cfif>

Creating a record

If you wanted to create a new Contact, you could simply pass CONTACT_ID=0 in the query string to contact_form.cfm. This would create an empty bean and therefore an empty form.

Now the question that should come to mind here is, "why should I read from the database when CONTACT_ID=0? The answer is, you shouldn't. With a simple update to the read() method, we can bypass running the query and re-populating the bean under this condition.

ContactDAO.cfc - read()
view plain print about
1<cffunction name="read" access="public" output="false" returntype="boolean">
2
3    <cfargument name="contact" required="true" type="Contact" hint="Contact bean" />
4
5    <cfset var qReadOne = "" />
6
7    <!--- Bypass the query and init() when CONTACT_ID = 0 --->
8    <cfif arguments.contact.getContactID() neq 0>
9
10        <cfquery name="qReadOne" datasource="#variables.DSN#">
11            SELECT
12                CONTACT_ID,
13                CATEGORY_ID,
14                FIRST_NAME,
15                LAST_NAME
16            FROM
17                CONTACTS
18            WHERE
19                CONTACT_ID = <cfqueryparam value="#arguments.contact.getContactID()#" cfsqltype="cf_sql_integer" />
20        </cfquery>
21
22        <cfif qReadOne.recordcount eq 0>
23
24            <cfreturn false />
25
26        <cfelse>
27
28            <cfset arguments.contact.init(
29                    CONTACT_ID = qReadOne.CONTACT_ID,
30                    CATEGORY_ID = qReadOne.CATEGORY_ID,
31                    FIRST_NAME = qReadOne.FIRST_NAME,
32                    LAST_NAME = qReadOne.LAST_NAME
33                    ) /
>

34
35        </cfif>
36
37    </cfif>
38
39    <cfreturn true />
40
41</cffunction>

The only change you'd make to contact_form.cfm would be to set the action page to something like contact_create.cfm in order to run the create() method instead of the update() method from ContactDAO.cfc.

contact_form.cfm - dynamic action
view plain print about
1<cfparam name="url.CONTACT_ID" type="numeric" default="0" />
2<cfparam name="request.actionPage" type="string" default="contact_create.cfm" />
3
4<cfif url.CONTACT_ID neq 0>
5    <cfset request.actionPage = "contact_update.cfm" />
6</cfif>
7
8<!--- List of Categories --->
9<cfset categories = application.categoryGateway.getCategories() />
10<!--- Instance of a Contact Bean --->
11<cfset contact = createObject("component", "Contact").init( CONTACT_ID = url.CONTACT_ID ) />
12<!--- Populate Contact Bean --->
13<cfset application.contactDAO.read( contact ) />
14
15<form name="contact_data" action="#request.actionPage#" method="post">

Now we're using the same contact_form.cfm to both Update and Create a Contact. The only difference is the output:

output

Category:

First Name:

Last Name:

Finally, here's the action page for creating a Contact:

contact_create.cfm
view plain print about
1<cfif structKeyExists(form, "processContact")>
2    
3    <cfset contact = createObject("component", "Contact").init(
4            CONTACT_ID = form.CONTACT_ID,
5            CATEGORY_ID = form.CATEGORY_ID,
6            FIRST_NAME = form.FIRST_NAME,
7            LAST_NAME = form.LAST_NAME
8            ) /
>

9
10    <cfset application.contactDAO.create( contact ) />
11
12 <cflocation url="contact_list.cfm" addtoken="false" />
13
14</cfif>

Does that look familiar? The only difference between contact_create.cfm and contact_update.cfm is that we're calling create() instead of update(). Even though the create() method doesn't require the CONTACT_ID, since we're passing data in name / value pairs, the order of the arguments doesn't matter. CONTACT_ID will be passed to the method, but it just won't be used for any part of the process that creates a record in the database.

Where's Hagrid?

When we first meet Rubeus Hagrid in "Harry Potter and The Sorcerer's Stone" (actually, in "The Philosopher's Stone") he is the Gamekeeper for Hogwarts School of Witchcraft and Wizardry. In the second book, "The Chamber of Secrets", Hagrid has kept his Staff position, but is now also an Instructor, teaching Care of Magical Creatures.

As the database layout stands now, each Contact can have only one Category. In order to properly enter Professor Hagrid into the Hogwarts database, we'll have to make some changes to the database tables to allow a single Contact record to map to multiple Category records and vice versa.

We'll also need a more Complex Data Access Object to handle this new many-to-many (*:*) relationship.