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

In our last segment we built the functionality to store our contacts to the database. Now we need a way to edit the information because after all, friends can become enemies, and enemies can become co-workers. We have a good structure in place and these changes will be simple.

Simple, that is, once we take care of a bit of housekeeping....

I just realized our event-handler for our contact form is under Contact.View. This seems a little silly, doesn't it? Even if you don't think so, it annoys me. So lets sort that really quick. Changing the form event to Contact.Form makes much more sense. We begin the change in the ModelGlue.xml file. Locate the Contact.View event-handler and rename it to Contact.Form. Then, in the same xml tagset, change the value of whichMenuIsCurrent to match.

<event-handler name="contact.form">
   <broadcasts>
   <message name="needContactForm" />
   <message name="needContactTypes" />
   </broadcasts>
   <views>
      &lt;include name="body" template="frmContact.cfm">
      <value name="whichMenuIsCurrent" value="contact.form" />
      &lt;/include>
   </views>
   <results>
      <result do="view.template" />
   </results>
</event-handler>

Note: if you are pasting from the above code sample, remember to change the & lt; to a proper < tag.

Lastly, open up the dspTemplate.cfm file located in *ContactManagerMG.views and alter the link to point from Contact.View to Contact.Form and the link text from Contact to Contact Form.

Before:

<li><a href="#myself#contact.view" <cfif whichMenuIsCurrent IS "contact.view">id="current"</cfif>>Contact</a></li>

After:

<li><a href="#myself#contact.form" <cfif whichMenuIsCurrent IS "contact.form">id="current"</cfif>>Contact Form</a></li>

If your application is set for production mode, you will need to reload the framework to pick up the changes in the XML file.

There, much better. Now back to our regularly scheduled programming. ( pun fully intended ).

Editing and removing contacts are actions that relate to a single contact. We have a list of contacts in our application and it would make sense to put the links for our new functions there.

Open dspContactList.cfm located inside the *ContactManagerMG.views directory. Our two links, Edit and Remove, go inside a new table column. Note we use the 'myself' value for the base of our links because 'myself'' pulls in the scriptname (index.cfm) and the eventValue parameter (event) automatically. Thus, if you wanted your application to live on contacts.cfm and to change the url parameter 'event' to 'ObeyMyCommand' making for a handy http://www.contact-o-matic.com/contacts.cfm?ObeyMyCommand=Contact.List, all of your links ( that use 'myself' ) would instantly reflect the changes and negate several hours of Find-And-Replace work. You can use the time saved to learn jQuery or something.

Here is a look at the dspContactList.cfm page.

<cfset myself = viewstate.getValue("myself") />
<cfset ContactList = viewstate.getValue("ContactList") />

<cfoutput>
   <cfif ContactList.recordcount IS 0 >
      -No Saved Contacts-<br />
   <cfelse>
      <table>
         <tr>
            <th>Name</th>
            <th>Type</th>
            <th>&nbsp; </th>
         </tr>   
      <cfloop query="ContactList">
         <tr>
            <td>#ContactName#</td>
            <td>#ContactType#</td>
            <td><a href="#myself#Contact.form&ContactID=#ContactID#">Edit</a> | <a

href="#myself#Contact.Remove&ContactID=#ContactID#">
Remove</a></td>
         </tr>
      </cfloop>
      </table>   
   </cfif>
</cfoutput>

Now to complete the Contact.Edit function. Up to this stage in our application, the contact form has been used to create contacts only. The new Contact.Form link passes a ContactID for a specific contact. We need to alter our controller function to pull the ContactID from the event scope, fetch the associated record from the database and stuff it inside our form bean.

From the top, we need a reference to the ContactFormBean and to the ContactService. We then set the ContactID value from the event (url) into the ContactFormBean and pass the whole shebang into a service function called loadContact. Finally, we place it back in the event where it will eventually find itself in a form.

<cffunction name="getContactForm" access="public" returnType="void" output="false">
      <cfargument name="event" type="any">
         <cfset var ContactFormBean = getModelGlue().getBean("ContactFormBean") />
       <cfset var ContactService = getModelGlue().getBean( "ContactService") />
         <cfset ContactFormBean.setContactID( arguments.event.getValue("ContactID") ) />         
         <cfset ContactService.loadContact( ContactFormBean ) />
         <cfset arguments.event.makeEventBean( ContactFormBean ) />
         <cfset arguments.event.setValue( "ContactFormBean", ContactFormBean ) />
   </cffunction>

Now, open the ContactService located inside *ContactManagerMG.model and add the new loadContact function. This function simply pulls in the ContactDAO, calls the read function and passes in the ContactFormBean.

<cffunction name="loadContact" access="public" returntype="void" output="false">
      <cfargument name="Contact" type="any" required="true"/>
      
         <cfset getContactDAO().read( arguments.Contact ) />

</cffunction>

We shouldn't have to alter the ContactDAO as the code has already been written.

If your app needs to be reloaded, then do so now. Try out the new editing capabilities now.

Wasn't that simple? Now we have a go at the remove function.

We added in the link on the dspContactList.cfm page to reference the Contact.Remove application event. Open up the ModelGlue.xml once again and add a new event-handler with the name of Contact.Remove. This event-handler will simply broadcast a message called needContactRemove and then use a result tag to direct the application flow back to Contact.List.

<event-handler name="Contact.Remove">
   <broadcasts>
      <message name="needRemoveContact" />
   </broadcasts>
   <results>
      <result do="Contact.List" redirect="true" />
   </results>
</event-handler>

Then, at the top of your ModelGlue.xml, register the new message and have it point to a function called removeContact.

<message-listener message="needRemoveContact" function="RemoveContact" />

Once complete, open up the Controller.cfc located at *ContactManagerMG.controller and add the removeContact function. This function will pull a new ContactFormBean from ColdSpring, set the ContactID from the passed ContactID in the event scope, and pass it to the ContactService.deleteContact() function.

<cffunction name="removeContact" access="public" returntype="void" output="false">
   <cfargument name="event" type="any" />
      <cfset var ContactFormBean = getModelGlue().getBean( "ContactFormBean" ) />
      <cfset var ContactService = getModelGlue().getBean( "ContactService") />
      <cfset ContactFormBean.setContactID( arguments.event.getValue("ContactID") ) />
      <cfset ContactService.removeContact( ContactFormBean ) />
</cffunction>

Finally, in ContactService, add the function for removeContact. This function takes the Contact component as an argument, gets a reference to the ContactDAO and calls the delete function with the Contact component as the sole argument.

<cffunction name="removeContact" access="public" returntype="void" output="false">
   <cfargument name="Contact" type="any" required="true"/>
   <cfset getContactDAO().delete( arguments.Contact ) />

</cffunction>

If your app needs to be reloaded, then do so now.. Click the remove link next to any contact in the list.

Bye Bye Contact!

Wow, that was pretty simple. We are reusing a lot of code and this means less typing. More importantly than economy of typing, code that has previously been written and tested can be used again without modification.

The completed code up to and including this section is attached below. Look for the 'download' link.

Comments
Small error in the beginning of this part:

Locate the Contact.View event-handler and rename it to Contact.View.
I think you mean:
Locate the Contact.View event-handler and rename it to Contact.Form.
# Posted By Stijn Dreezen | 2/22/07 10:32 AM
Thank you Stijn. I have made the correction in the post.


dw
# Posted By Dan Wilson | 2/22/07 10:44 AM
How does ContactFormBean get the ContactName value from the DB on an edit action? It currently shows ContactNamew as empty string? It is possible that I screwed up an excellent tutorial(wouldn't be the first time) and missed a key part along the way!

Thanks
# Posted By robb | 3/1/07 12:30 PM
Robb,

Inside the ContactDao, in the read function there is a query that pulls the contact record out of the database. Immediately following the query are three lines that set the value from the query into the ContactFormBean. See below:

<cfset arguments.Contact.setContactID( ReadQuery.ContactID ) />
<cfset arguments.Contact.setContactName( ReadQuery.ContactName ) />
<cfset arguments.Contact.setContactTypeID( ReadQuery.ContactTypeID ) /


When the function ends, the bean has been populated and processing continues .


Was that helpful?

DW
# Posted By Dan Wilson | 3/1/07 1:26 PM
Wow that was an awesomely quick response! I think I have an error in my code though, because when I go to the URL http://127.0.0.1/contactmanagerMG/index.cfm?event=...
and dump contactFormBean.getMemento() I get contactID but have empty strings for contactName and ContactTypeID. For some reason I want to add a new listener contact.edit that would make sure that contactFormBean is being loaded from DB. Would this be a screwed up approach? I thank you for doing this tutorial, it has been very helpful.
# Posted By robb | 3/1/07 2:46 PM
Robb,

Look in the controller.cfc at the getContactForm.

<cfset var ContactFormBean = getModelGlue().getBean("ContactFormBean") />
<cfset var ContactService = getModelGlue().getBean( "ContactService") />
<cfset ContactFormBean.setContactID( arguments.event.getValue("ContactID") ) />         
<cfset ContactService.loadContact( ContactFormBean ) />
<cfset arguments.event.makeEventBean( ContactFormBean ) />
<cfset arguments.event.setValue( "ContactFormBean", ContactFormBean ) />




You can see we pull in the ContactFormBean and the ContactService. Then we set the value of ContactID from the event into the ContactFormBean. Following, we pass the ContactFormBean to the ContactService.loadContact() function and get back a populated bean.

If you are getting the contactID in your memento dump, then that means the ContactID is coming though the event just fine. You may want to have a look at the loadContact method in your service.

The source code is attached to this blogpost. You can get it by clicking the download link.


Hope this helps,

Dan Wilson
# Posted By Dan Wilson | 3/1/07 10:30 PM
My getContactForm function was not the same as your post, so somehow I screwed up. It works now and I got it!
# Posted By robb | 3/2/07 12:56 AM
Thanks on that...It worked on me..
Mazda wheels - http://www.automotivemazparts.com/mazda-wheels/
# Posted By Danny Brians | 9/27/07 5:26 AM
Hi, thanks for the great tutorials. I am new to all this so I might be asking a silly question, but I was wondering why are you using functions that operate solely on their arguments and don't return any values? For example:
<cffunction name="loadContact" access="public" returntype="void" output="false">
<cfargument name="contact" type="any" required="true"/>
<cfset getContactDAO().read( arguments.contact ) />
</cffunction>
Does it offer some advantage over a code simply returning object both from the contactDAO.read() and contactService.loadContact() method?
For example if I implemented some views in Flex, methods in the service component returning values could be easily consumed remotely:
<cffunction name="loadContact" access="remote" returntype="any" output="false">
<cfargument name="contact" type="any" required="true"/>
<cfreturn getContactDAO().read( arguments.contact ) />
</cffunction>
Just I'm thinking about integrating MG with Flex and it looks that the service component could also serve remote requests. Or am I missing the point here?
# Posted By strix | 1/12/08 5:39 PM
strix,

You are on the right track. The ContactOMatic is an academically simple application with emphasis on using ModelGlue and ColdSpring effectively.

If I was building the model/service layer for a real world application, there are a number of things I might do differently. For example, if I was going to expose part of my model remotely, I would probably not use the UserService as you see it in code. I would have a proper remote service. (Check into the ColdSpring remote service factory. )

Of course, a remote service can call other services/model objects as is appropriate for the transaction. The methods in those objects could operate on the reference, or return it, as long as the value was in scope for the remote function to serialize the data and return it over the wire.

Thanks for bringing this point up. I bet it will help someone else.

DW
# Posted By Dan Wilson | 1/12/08 8:25 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.001. Contact Blog Owner