So you want to create a ModelGlue:Unity application? ( Part 7 )
We are going to break this up into phases starting with the contact types. At the end of this series, our contact types will come from the database and we will have an even greater appreciation for ModelGlue and ColdSpring.
The last article gave the spec for your database tables, Contact and ContactType. Add in 3 dummy records to the ContactType table. I used Friend, Enemy and Co-worker.
Set up your datasource in the ColdFusion administrator. I used the data source name of 'ContactOMatic'. Test the connection and move on.
Now we will need this datasource name inside of our cfqueries. Can anyone guess where we will keep the DSN? Thats right, in ColdSpring. Give yourself a cookie if you got that one right.
Before we begin, change the ColdSpring.xml ModelGlueConfiguration reload setting to true.
ModelGlue has a standard component called a SimpleConfig. The purpose of the simple config is to hold on to a collection of values and this component is easily configured in ColdSpring. The path for SimpleConfig is ModelGlue.bean.CommonBeans.SimpleConfig, so add a configuration to ColdSpring for this. We will refer to this as AppConfig and it will hold our DSN and an AppTitle.
<property name="Config">
<map>
<entry key="AppTitle"><value>Contact-O-Matic</value></entry>
<entry key="dsn"><value>ContactOMatic</value></entry>
</map>
</property>
</bean>
Run your application. If you have an error at this point, check the exact path to the SimpleConfig component. No errors? Perfect!
Simple enough so far, right? Now we shall write our ContactTypeGW. This component will pull a query of ContactTypes from the database. There are three functions in the ContactTypeGW.
- setAppConfig - this function provides the way for ColdSpring to stick the AppConfig component in the ContactTypeGW
- getAppConfig - this function provides a way for us to reference the AppConfig component inside the ContactTypeGW
- getContactTypeQuery - this function returns the ContactTypes from the database
Your ContactTypeGW should look like this:
<cffunction name="getContactTypeQuery" access="public" output="false" returntype="query">
<cfset var ContactTypeQuery = "" />
<cfquery name="ContactTypeQuery" datasource="#getAppConfig().getConfig().dsn#">
SELECT ContactTypeID, ContactType
FROM ContactType
</cfquery>
<cfreturn ContactTypeQuery />
</cffunction>
<cffunction name="getAppConfig" access="public" output="false" returntype="any">
<cfreturn variables.instance.AppConfig />
</cffunction>
<cffunction name="setAppConfig" access="public" output="false" returntype="void">
<cfargument name="AppConfig" type="any" required="true" />
<cfset variables.instance.AppConfig = arguments.AppConfig />
</cffunction>
</cfcomponent>
Note for the DSN we used 'getAppConfig().getConfig().dsn', when we want to access the AppTitle later, we'll use 'getAppConfig().getConfig().AppTitle'. (If you haven't seen this syntax before, getAppConfig() returns a reference to the AppConfig Object, which has a getConfig() function returning a struct. Then we access a property of the struct using dot notation.)
Now, adjust the ContactService to call ContactTypeGW. Remove the following functions: init, getContactTypes and setContactTypes. Add get and set functions for our ContactTypeGW. Then add the proper function for getContactTypes. It should return a query so be sure to adjust the returntype to 'query'
Your ContactManangerService should look like this:
<!---
PROPERTIES
--->
<cfset variables.instance = StructNew() />
<cffunction name="getContactTypes" access="public" returntype="query" output="false">
<cfset var ContactTypes = getContactTypeGW().getContactTypeQuery() />
<cfreturn ContactTypes />
</cffunction>
<cffunction name="getContactTypeGW" access="public" output="false" returntype="any">
<cfreturn variables.instance.ContactTypeGW />
</cffunction>
<cffunction name="setContactTypeGW" access="public" output="false" returntype="void">
<cfargument name="ContactTypeGW" type="any" required="true" />
<cfset variables.instance.ContactTypeGW = arguments.ContactTypeGW />
</cffunction>
<cffunction name="setContactList" access="public" returntype="void" output="false">
<cfargument name="ContactList" type="array" required="true" />
<cfset variables.instance.ContactList = arguments.ContactList />
</cffunction>
<cffunction name="getContactList" access="public" returntype="array" output="false">
<cfreturn variables.instance.ContactList />
</cffunction>
</cfcomponent>
Now adjust ColdSpring to account for the new gateway functionality. Configure the bean definition for ContactTypeGW and add the AppConfig property. Replace the ContactType map with the ContactTypeGW bean The changed lines should look like this:
<property name="ContactList">
<list></list>
</property>
<property name="ContactTypeGW"><ref bean="ContactTypeGW" /></property>
</bean>
<bean id="ContactTypeGW" class="ContactManagerMG.model.ContactTypeGW">
<property name="AppConfig"><ref bean="AppConfig" /></property>
</bean>
Finally, our contact form was expecting a struct, it will now get a query so adjust the ContactType section accordingly
<option value="#ContactType#" <cfif ContactFormBean.getContactType() IS ContactType>selected</cfif>>#ContactTypes.ContactType#</option>
</cfloop>
Remember to reinitialize your application and now run your code. You should now see the ContactTypes defined in your database
It seems like we touched a few files to make this change. We altered the ColdSpring.xml file to have an application configuration object, added in our ContactTypeGW and then stitched them together. We also had to adjust our form to account for a query in place of the struct. Note we did not touch our Controller nor our ModelGlue.xml file. Since the flow of the application did not change, those files remain the same and happily work with the new functionality of the application. This is an example of the benefits of compartmentalized, or encapsulated, code.
A zip of all files is included with this article.
Next, we will create the contact persistance and write our contacts to the database







FYI: There's a reference to ContactManangerService in here, instead of ContactService. It might be a little confusing.
Also, the line in the bean ContactTypeGW:
<property name="AppConfig"><ref bean="AppConfig" /></property>
What does that do and how does it work?
Dan
The service is in fact a layer in between your controller and your model. It is important not to clog the controller with too much logic. The point of the Controller in the Model View Controller (MVC) pattern is to direct traffic into your model.
There is nothing stopping you from adding all the code you want to in your controller and for simple applications, such as the Contact-O-Matic, this might seem like a more streamlined approach. The reason for the separation is to keep your logic inside the model, rather than the controller. ModelGlue is used to build enterprise class applications and this layered approach helps to compartmentalize the complexity in an application as well as reduce the dependancies on a specific framework.
In fact, In less than an hour, I could convert the entire Contact-O-Matic to a Mach-II or Fusebox application if I so wished.
To your other question, the Property tag simply defines a property in the ContactTypeGW. ColdSpring will pass the referenced bean 'AppConfig' into the ContactTypeGW when it is created.
This keeps the actual configuration of important details such as DSN from being sprinkled throughout the application. Since these details are encapsulated in the AppConfig, the same bean can be referenced by other components, thereby keeping all consumers of the AppConfig in Sync.
Let me know if this is not clear.
Dan Wilson
Is there anyway of testing to see if a CFC has been injected with another cfc/bean?