So you want to create a ModelGlue:Unity application? ( Part 8 )

In our last series, we moved the ContactTypes from a ColdSpring configured struct, to a database table. This set the stage to move the rest of our persisted data to the database.

As we begin this series our main goal is to have our contacts and lists of contacts stored in our database. Originally, we used in-memory storage as it allowed us a functional application without a database.

In this series, we will introduce two new files, ContactDAO and ContactGW (GW = Gateway). Both components will reside in the *ContactManagerMG.model directory. The ContactDAO will handle the DB work for Creating, Reading, Updating and Deleting our Contacts. Incidentally, this group of functionality is commonly referred to as C.R.U.D. The ContactGW will handle the database work for pulling queries of Contacts. We'll have a function called getContactQuery().

Here is a look at the ContactDAO. Note: we reference the AppConfig inside our ContactDAO so we can use the Application Configuration defined in ColdSpring. While we mostly care about the DSN for now, the flexibility to pull other configuration parameters will come in handy later.

view plain print about
1<cfcomponent>
2
3    <cffunction name="Create" access="public" output="false">
4        <cfargument name="Contact" required="yes" />
5        <cfset var CreateQuery = "" />
6    
7            <cfquery name="CreateQuery" datasource="#getAppConfig().getConfig().dsn#">
8    
9                INSERT INTO Contact
10                ( ContactName, ContactTypeID )
11                VALUES
12                (
13                    <cfqueryparam value="#arguments.Contact.getContactName()#" cfsqltype="cf_sql_varchar">,
14                    <cfqueryparam value="#val( arguments.Contact.getContactTypeID() )#" cfsqltype="cf_sql_numeric">                    
15                )            
16    
17            </cfquery>
18        
19    </cffunction>
20
21    <cffunction name="Read" access="public" output="false">
22        <cfargument name="Contact" required="yes" />
23        <cfset var ReadQuery = "" />
24        
25        <cfquery name="ReadQuery" datasource="#getAppConfig().getConfig().dsn#">
26    
27            SELECT ContactID, ContactName, ContactTypeID
28            FROM Contact
29            WHERE ContactID = <cfqueryparam value="#val( arguments.Contact.getContactID() )#" cfsqltype="cf_sql_numeric">
30    
31        </cfquery>
32
33        <cfset arguments.Contact.setContactID( ReadQuery.ContactID ) />
34        <cfset arguments.Contact.setContactName( ReadQuery.ContactName ) />
35        <cfset arguments.Contact.setContactTypeID( ReadQuery.ContactTypeID ) />
36
37    </cffunction>
38
39    <cffunction name="Update" access="public" output="false">
40        <cfargument name="Contact" required="yes" />
41        <cfset var UpdateQuery = "" />
42        
43        <cfquery name="UpdateQuery" datasource="#getAppConfig().getConfig().dsn#">
44    
45            UPDATE Contact
46                SET
47                    ContactName = <cfqueryparam value="#arguments.Contact.getContactName()#" cfsqltype="cf_sql_varchar">,
48                    ContactTypeID = <cfqueryparam value="#arguments.Contact.getContactTypeID()#" cfsqltype="cf_sql_numeric">
49            WHERE ContactID = <cfqueryparam value="#arguments.Contact.getContactID()#" cfsqltype="cf_sql_numeric">
50                
51        </cfquery>        
52    
53    </cffunction>
54
55    <cffunction name="Delete" access="public" output="false">
56        <cfargument name="Contact" required="yes" />
57        <cfset var DeleteQuery = "" />
58        
59        <cfquery name="DeleteQuery" datasource="#getAppConfig().getConfig().dsn#">
60    
61            DELETE FROM Contact
62            WHERE contactID = <cfqueryparam value="#arguments.Contact.getContactID()#" cfsqltype="cf_sql_numeric">
63    
64        </cfquery>
65    
66    </cffunction>
67
68
69    <cffunction name="getAppConfig" access="public" output="false" returntype="any">
70        <cfreturn variables.instance.AppConfig />
71    </cffunction>
72    
73    <cffunction name="setAppConfig" access="public" output="false" returntype="void">
74        <cfargument name="AppConfig" type="any" required="true" />
75        <cfset variables.instance.AppConfig = arguments.AppConfig />
76    </cffunction>
77    
78    
79    
80
81</cfcomponent>

Here is a look at the ContactGW. Once again we reference the AppConfig.

view plain print about
1<cfcomponent>
2
3
4 <cffunction name="getContactQuery" access="public" output="false" returntype="query">
5 <cfset var ContactQuery = "" />
6
7 <cfquery name="ContactQuery" datasource="#getAppConfig().getConfig().dsn#">
8 SELECT C.ContactID, c.ContactName, c.ContactTypeID, ct.ContactType
9 FROM Contact c LEFT JOIN ContactType ct ON c.ContactTypeID = ct.ContactTypeID
10 </cfquery>
11
12 <cfreturn ContactQuery />
13
14 </cffunction>
15
16 <cffunction name="getAppConfig" access="public" output="false" returntype="any">
17 <cfreturn variables.instance.AppConfig />
18 </cffunction>
19
20 <cffunction name="setAppConfig" access="public" output="false" returntype="void">
21 <cfargument name="AppConfig" type="any" required="true" />
22 <cfset variables.instance.AppConfig = arguments.AppConfig />
23 </cffunction>
24
25</cfcomponent>

Now we add each component to our ColdSpring configuration. Each component takes one Property, the AppConfig object. Add both beans as new property tags to the existing ContactService bean definition. We will also remove the references to the old Array based ContactList. Let's do a little housecleaning on our ColdSpring file shall we?

Add the definitions for the components

view plain print about
1<bean id="ContactDAO" class="ContactManagerMG.model.ContactDAO">
2 <property name="AppConfig"><ref bean="AppConfig" /></property>
3 </bean>
4
5 <bean id="ContactGW" class="ContactManagerMG.model.ContactGW">
6 <property name="AppConfig"><ref bean="AppConfig" /></property>
7 </bean>

Remove the following lines from the existing ContactService definition:

view plain print about
1<property name="ContactList">
2    <list></list>
3</property>

Add in the property tags for our new components.

view plain print about
1<bean id="ContactService" class="ContactManagerMG.model.ContactService">
2        <property name="ContactTypeGW"><ref bean="ContactTypeGW" /></property>
3        <property name="ContactDAO"><ref bean="ContactDAO" /></property>
4        <property name="ContactGW"><ref bean="ContactGW" /></property>        
5    </bean>

Once complete, ColdSpring will run setContactDAO() and setContactGW() on the ContactService component and stuff the proper object in our service for us. We need to change the service a little now to account for this new functionality. Open up the ContactService and make the following changes:

Remove the getContactList() and setContactList() functions. These functions used to manage our Array based contact list from ColdSpring, with our new Database driven Contact-O-Matic, we won't need them any longer.

Remove:

Now add in the get/set functions for each component. While we are in here, also add in a new function to get our ContactList from our new ContactGW. The function on the ContactGW is called getContactQuery(). When complete, you'll have removed two functions and added five. Examples of the new functions are below:

view plain print about
1<cffunction name="getContactList" output="false" access="public" returntype="query" hint="I return a list of contacts">
2        <cfreturn getContactGW().getContactQuery() />
3    </cffunction>
4    
5    <cffunction name="getContactDAO" access="public" output="false" returntype="any">
6        <cfreturn variables.instance.ContactDAO />
7    </cffunction>
8    
9    <cffunction name="setContactDAO" access="public" output="false" returntype="void">
10        <cfargument name="ContactDAO" type="any" required="true" />
11        <cfset variables.instance.ContactDAO = arguments.ContactDAO />
12    </cffunction>
13
14    <cffunction name="getContactGW" access="public" output="false" returntype="any">
15        <cfreturn variables.instance.ContactGW />
16    </cffunction>
17    
18    <cffunction name="setContactGW" access="public" output="false" returntype="void">
19        <cfargument name="ContactGW" type="any" required="true" />
20        <cfset variables.instance.ContactGW = arguments.ContactGW />
21    </cffunction>

Now our service has references to the ContactDAO and the ContactGW, in turn both the ContactDAO and ContactGW have references to our AppConfig. All the plumbing for the database interaction is complete.

Previously, we stored the ContactType in our ContactFormBean. This was acceptable when referential integrity was not on our list. Now, we need to modify the Contact Form and the Contact Form Bean to refer to the ContactTypeID instead of simply the ContactType. In the form itself, all we need to do is switch the reference. Inside the Bean there is a tiny bit more to it. We will start with the form.

The select tag block should be changed as follows

view plain print about
1Type: <select name="ContactTypeID">
2    <cfloop query="ContactTypes">
3 <option value="#ContactTypeID#" <cfif ContactFormBean.getContactTypeID() IS ContactTypeID>selected</cfif>>#ContactTypes.ContactType#</option>
4 </cfloop>
5    </select>

Now open the ContactFormBean and change all the ContactType references to ContactTypeID. A find and replace would be nice here. A simply Find and Replace should make 11 changes...

There are two references in the Init() function, one in the validate function and also a get/set block at the bottom. Each of these should be changed to ContactTypeID.

The completed ContactFormBean is here:

view plain print about
1<cfcomponent
2    displayname="ContactFormBean"
3    output="false"
4    hint="A bean which models the ContactFormBean form.">

5
6
7    <!---
8    PROPERTIES
9    --->

10    <cfset variables.instance = StructNew() />
11
12    <!---
13    INITIALIZATION / CONFIGURATION
14    --->

15    <cffunction name="init" access="public" returntype="ContactManagerMG.model.ContactFormBean" output="false">
16        <cfargument name="ContactID" type="string" required="false" default="" />
17        <cfargument name="ContactName" type="string" required="false" default="" />
18        <cfargument name="ContactTypeID" type="string" required="false" default="" />
19
20        <!--- run setters --->
21        <cfset setContactID(arguments.ContactID) />
22        <cfset setContactName(arguments.ContactName) />
23        <cfset setContactTypeID(arguments.ContactTypeID) />
24
25        <cfreturn this />
26     </cffunction>
27
28    <!---
29    PUBLIC FUNCTIONS
30    --->
    
31    <cffunction name="getMemento" access="public"returntype="struct" output="false" >
32        <cfreturn variables.instance />
33    </cffunction>
34
35    <cffunction name="validate" access="public" returntype="boolean" output="false">
36        <cfargument name="Errors" type="struct" required="true" />
37        <cfset tempErrors = structNew() />
38
39        <cfif NOT len( trim( getContactName() ) ) >
40            <cfset tempErrors['ContactName'] = "Please enter a name for your contact" />
41        </cfif>
42        <cfif NOT len( trim( getContactTypeID() ) )>
43            <cfset tempErrors['ContactTypeID'] = "Please enter a contact type for your contact" />
44        </cfif>        
45        
46        <cfif structCount( tempErrors ) >
47            <cfset structAppend( arguments.Errors, tempErrors ) />
48            <cfreturn false />
49        <cfelse>
50            <cfreturn true />        
51        </cfif>
52        
53    </cffunction>
54
55    <!---
56    ACCESSORS
57    --->

58    <cffunction name="setContactID" access="public" returntype="void" output="false">
59        <cfargument name="ContactID" type="string" required="true" />
60        <cfset variables.instance.ContactID = arguments.ContactID />
61    </cffunction>
62    <cffunction name="getContactID" access="public" returntype="string" output="false">
63        <cfreturn variables.instance.ContactID />
64    </cffunction>
65
66    <cffunction name="setContactName" access="public" returntype="void" output="false">
67        <cfargument name="ContactName" type="string" required="true" />
68        <cfset variables.instance.ContactName = arguments.ContactName />
69    </cffunction>
70    <cffunction name="getContactName" access="public" returntype="string" output="false">
71        <cfreturn variables.instance.ContactName />
72    </cffunction>
73
74    <cffunction name="setContactTypeID" access="public" returntype="void" output="false">
75        <cfargument name="ContactTypeID" type="string" required="true" />
76        <cfset variables.instance.ContactTypeID = arguments.ContactTypeID />
77    </cffunction>
78    <cffunction name="getContactTypeID" access="public" returntype="string" output="false">
79        <cfreturn variables.instance.ContactTypeID />
80    </cffunction>
81
82</cfcomponent>

Finally, we need to change how the contacts are saved. In the beginning, we simply appended the ContactFormBean object into an Array of ContactFormBean object and iterated over the array for our ContactList. Now, we need to change the controller function to call the service. We will let the service decide how to handle the save. In the controller located at *ContactManagerMG.controller modify the isvalid branch to call a ContactService method called saveContact()

view plain print about
1<cfif isValid >
2         <cfset ContactService.saveContact( ContactFormBean ) />
3         <cfset arguments.event.addResult("Success") />
4     <cfelse>
5         <cfset arguments.event.setValue("ErrorStruct", ErrorStruct) />
6          <cfset arguments.event.addResult("Failure") />
7     </cfif>

Now add the saveContact() function into your ContactService. This function will accept a Contact object, being our ContactFormBean, and check to see if the ContactID has been assigned. If so, we will update the record, if not, we will create the record. Your saveContact() function should look like this:

view plain print about
1<cffunction name="saveContact" access="public" returntype="void" output="false">
2        <cfargument name="Contact" type="any" required="true"/>
3        
4        <cfif val( Contact.getContactID() ) GT 0 ><!--- Update --->
5            <cfset getContactDAO().update( arguments.Contact ) />
6        <cfelse><!--- Insert --->
7            <cfset getContactDAO().create( arguments.Contact ) />
8        </cfif>
9
10 </cffunction>

After changing the save contact flow, we have closed the circle for adding / updating the individual contacts. Remaining, is the ContactList page.

We will now be returning a query and ourcontact list page used to operate on an array. Thus, we must adjust.

Change your dspContactList.cfm page to use a query. Your completed list page will look as follows:

view plain print about
1<cfset myself = viewstate.getValue("myself") />
2<cfset ContactList = viewstate.getValue("ContactList") />
3
4<cfoutput>
5    <cfif ContactList.recordcount IS 0 >
6        -No Saved Contacts-<br />
7    <cfelse>
8        <table>
9            <tr>
10                <th>Name</th>
11                <th>Type</th>
12            </tr>    
13        <cfloop query="ContactList">
14            <tr>
15                <td>#ContactName#</td>
16                <td>#ContactType#</td>
17            </tr>
18        </cfloop>
19        </table>    
20    </cfif>
21</cfoutput>

Reset your application and run your new Database Powered Contact-O-Matic. If you've followed all the steps, (and I haven't left any out) you now have a proper application.

We touched a good chunk of the application and I trust this has been a good refresher into the inner workings of our Contact-O-Matic. In our next series, we will include the mechanics to delete contacts from the database.

Download Download

There are no comments for this entry.

Add Comment Subscribe to Comments

7/14/08 12:15 PM # Posted By Tom

Just a brief point; when you Change the dspContactList.cfm page to use a query, you forget to amend the field named contactType to contactTypeID..