Creating the Gateway Object - ColdBox Series Part 7a

Geez, it doesn't seem like we are doing much with ColdBox in this ColdBox series, does it?

Of course, to get to the point where we are actually using something like ColdBox to manage the communication between our model and our views, we need to have a model and some views.

So in the last post we discussed creating our Domain Objects(beans). So now we have two domain objects. We have our TodoList bean and our TodoItem bean.

You may recall our UML Diagram:

In our UML we decided that we were going to have a single Gateway object that would manage all of our database transactions. Today we are going to build that object.

Again, we are going to use the Illudium PU-36 Code Generator to make our tasks easier. But since the Code Generator does not come with the template needed to build our Gateway Object, we are going to be copying and pasting from the DAO/gateway code that it generates. We will also need to add some methods of our own.

If you need a refresher on how to use Illudium PU-36, check out the post where we built our domain objects.

Creating the TodoGateway

First, we need to create a gateway CFC into which we can copy and paste our code. So in the /model/todos directory, create a file called todoGateway.cfc.


    <cfcomponent displayname="TodoGateway" output="false">

    </cfcomponent>

Next, launch Illudium PU-36 and generate code for the TodoList table of our database.

The code generator creates a lot of functions, we are going to keep things simple. Under the tab labeled "DAO", get just the CRUD (Create, Read, Update, Delete) methods. Copy and paste those method from the code generator into our new todoGateway.cfc. Do not worry about the init() method for now.

Now our gateway has four methods in it, but if you look back up at our UML, we are going to be adding four more CRUD methods as well (for the list items). Since we cannot have methods with the same names, we need to rename our CRUD methods to represent the type of domain object they will be working with.

So create() becomes createList(), update() becomes updateList(), etc.

We now have 4 new methods in our todoGateway.

Let's add the CRUD for the todo items. Remember, we are going to change the names of the methods to createItem(), updateItem(), etc.

Great! Now we have 8 methods for managing persistence of our lists and items.

We need to copy one more method from Illudium. queryRowToStruct() is a method used by a few of the methods in our gateway, so just copy that method from the code generator (it is under the "gateway" tab of the item or list generated code).

This is where things might get weird

Now for the part that is somewhat new to me, and probably to many of you as well. We already set up out TodoList with an Array to hold ToDoItems, but how do we get those items into the TodoList?

In the past, I have been using Transfer to manage this for me. Transfer is AWESOME at making this type of object composition easy. But, we are not using Transfer for this project, because we need to know how to do this without an ORM.

Modifying the readList() Method to Add Composition

Let's start simple. I want to set up my gatewayObject's readList() method to return a List object and that list object's Items array properties will be populated with all of the item objects that belong to that list.

Here is what my readList() method looks like now:


    <cffunction name="readList" access="public" output="false" returntype="void">
        <cfargument name="list" type="model.todos.list" required="true" />

        <cfset var qRead = "" />

        <cfset var strReturn = structNew() />

        <cftry>
            <cfquery name="qRead" datasource="#variables.dsn#">
                SELECT    listid,    name, priority
                FROM    lists
                WHERE    listid = <cfqueryparam value="#arguments.list.getlistid()#" CFSQLType="cf_sql_integer" />
            </cfquery>

            <cfcatch type="database">
                <!--- leave the bean as is and set an empty query for the conditional logic below --->
                <cfset qRead = queryNew("id") />
            </cfcatch>
        </cftry>

        <cfif qRead.recordCount>
            <cfset strReturn = queryRowToStruct(qRead)>
            <cfset arguments.list.init(argumentCollection=strReturn)>
        </cfif>
    </cffunction>

This method is expecting a List bean to be passed in ready for population. The list bean will have been created in our Service Layer (which we will get to later). For right now just know that the bean will have already been created, but it is an empty bean and we are about to populate it from the database.

Next, the method sets some local variables. qRead will be used to store our query and strReturn is a struct for holding the query results as a struct.

Our query is then run and the results are stored in the qRead variable. If the query fails for database reasons, the <cfcatch> tag will place an empty query into the query variable so that the bean is left alone.

Finally, the query is examined for values, if there is a record, then that record is converted to a struct and passed into the bean's init() method as an argument, the bean's int() method will populate the bean, an we are done. We have received a List from the database. Now we just need the items.

Adding Items to the List Object

Well, to get all of the items for our list we need a query that does just that. We could just add it to the readList() method, but there may come a time that we want to get all of our items as a query without needing or wanting to get the list to do it. Plus, it feels better to me to separate that functionality. So we are going to create a getAllItemsAsQuery() method.


<cffunction name="getAllItemsAsQuery" access="public" output="false" returntype="query">
        <cfargument name="id" type="numeric" required="true" hint="pass in the list id" />
        
        <cfset var qItems = "" />
        
        <cftry>
            <cfquery name="qItems" datasource="#variables.dsn#">
                SELECT     itemid, title, dueDate
                FROM    items
                WHERE     listid_fk = <cfqueryparam value="#arguments.id#" cfsqltype="cf_sql_integer" />
            </cfquery>
            
            <cfcatch type="database">
                <cfset qItems = QueryNew("id") />
            </cfcatch>
        </cftry>
        
        <cfreturn qItems />
    </cffunction>

Now, we need to request that list, loop over it and set the objects for the Items array inside the List object. So first we will add three new local variables to the top of the readList() method:


    <cfset var qItems = "" />
    <cfset var tmpObj = "" />
    <cfset var strTemp = StructNew() />

Next, at the bottom of the readList() method we will add:


    <cfset qItems = getAllItemsAsQuery(arguments.list.getlistid()) />
    
    <cfloop query="qItems">
        <cfset strTemp = queryRowToStruct(qItems, currentrow) />
        <cfset tmpObj = createObject("component","model.todos.item").init(argumentCollection=strTemp) />
        <cfset arguments.list.addItem(tmpObject) />
    </cfloop>

Here we are calling the new method we just created to get a query of all of the items for this list. Then we are looping over that query and creating objects for each item.

For each loop we are adding the contents of the query row to the strTemp struct (note: the second argument of the queryRowToStruct() method tells it which query row to use), then it creates a temporary object (which is an item bean) and passes the strTemp struct into the init() method to populate the bean.

Finally it uses the addItem() method in the List bean to add the item bean to the internal items array.

Wait a second! What addItem() method?

Come on, let's go add it. Add this method to your list.cfc:


    <cffunction name="addItem" access="public" returntype="void" output="false">
        <cfargument name="bean" required="true" type="model.todos.item" />
        
        <cfset ArrayAppend(variables.instance.items, arguments.bean) />
    </cffunction>

To Be Continued...

OK, that's all for today. This post is getting longer than I expected, so I am going to break it into two (or three). We still need to modify our updateList() and deleteList() methods to handle our object composition. Then, I think we can finally start working with ColdBox and ColdSpring.

Thanks for your patience. I hope you are getting something out of this basic OO stuff, I know I am. Please leave me comments and let me know how you think things are going.

Comments
Mike Mongeau's Gravatar This series is great, Jason. Thanks for taking the time to blog about it.

My only confusion on this post is with the addItem() function you created for the list bean. You are appending the item bean to the array variables.items, but I don't see where that exists. The list bean only creates variables.instance.items.

Also, could the list creation have been done differently? The list bean contains a setItems() function which accepts an array. Could the readList() function simply create a temporary array of the items from the query and then pass that array to setItems() ?
# Posted By Mike Mongeau | 10/21/08 1:47 PM
Jason Dean's Gravatar @Mike, Thanks for the comment. I will address you questions in order:

1. oops
2. Yes

Thanks for pointing out the error. The addItem() method is supposed to append to variables.instance.items, not variables.items. I will make the correction.

And yes, we could have created a temporary array of item objects in the readList() method and then passed the whole array to the setItems() method. To - may - to, To = mah - to :)
# Posted By Jason Dean | 10/21/08 6:34 PM
doug boude's Gravatar Jason, just curious why you didn't use Coldspring to create your Item beans instead of using createobject?
# Posted By doug boude | 11/5/08 12:59 PM
Jason Dean's Gravatar @Doug. Thanks for the question. It is a good one. A couple of reason:

1. By using ColdSpring from within my List Bean, I would be coupling my bean to the existence of the ColdSpring Framework.
2. My understanding is that ColdSpring is for managing Singletons. Beans are Transient objects. Here is the description from the ColdSpring website where Singletons are being compared to Transient Objects:

"So why do we need to discuss this difference? Because ColdSpring is really optimized to manage Singletons. It is not ideal for managing Transient CFCs for a number of reasons: ..."

http://www.coldspringframework.org/coldspring/exam...
# Posted By Jason Dean | 11/5/08 1:19 PM
Greg Franklin's Gravatar Jason,

Could you post the source code for the todoGateway.cfc that you made with the code generator? The code generator doesn't work in my environment (Railo / Tomcat / Mac). I was humming along fine with your tutorials until Part 8d, where you mention the need to modify todoGateway.createList() and createItem(). I don't have those 8 other functions created in this Part 7a of your series. You also mention in Part 9b "I will probably wrap this series up and attach the finished application". Have you attached the finished application to one of your articles? I can't find it.

Thanks,

GF
# Posted By Greg Franklin | 11/10/09 7:12 AM
Jason Dean's Gravatar @Greg,

Thanks for the comment. No, I never did wrap up the application. Sorry. I got distracted and never finished it. Now that it has been almost a year since my last post on it, I honestly doubt that I will. Hopefully if you can make it through the existing content, you will be able to figure out the rest, but if you have specific questions, please let me know.

As for the code, part 8d has an attachment with almost everything, including the todoGateway.cfc

http://www.12robots.com/index.cfm/2008/11/15/Addin...
# Posted By Jason Dean | 11/10/09 7:58 AM
Greg Franklin's Gravatar @Jason,

Yeah, I realize I'm getting into the game a year late, but your tutorials are really helpful. I spent a full day and a chunk of money in training with Luis Majano at CFUNITED 2009 and didn't really get it, but I am starting to get the hang of it with your tutorials!

The link from Part 8d is the same url as the link from Part 6 -- there are only 2 CFCs in that .zip file. If you can find more files and post them, that would be a big help, I am basically missing the functions that are generated for the todoGateway.cfc. Thanks.
# Posted By Greg Franklin | 11/10/09 8:26 AM
Jason Dean's Gravatar @greg,

The attachment in the 8d post has a todoGateway.cfc in the /model folder that has all of the generated functions in it. And it has a lot more than two .cfc files in it. Here is the direct link.

http://www.12robots.com/enclosures/todos2.zip
# Posted By Jason Dean | 11/10/09 9:03 AM
Greg Franklin's Gravatar I was looking at the linked text "I am attaching the entire application to the post.", rather than the "Download" link at the footer of the article. The former links to the zip containing only 2 CFCs. I see the "Download" link now, my apologies for not seeing that. You may want to correct the link in the content above it, but no biggie. Thx for your guidance!
# Posted By Greg Franklin | 11/10/09 9:17 AM
Greg Franklin's Gravatar I have a question and it is rather crucial to my understanding of Coldbox. I'm not sure if it is a failure to grasp OO, or (more likely) a lack of understanding of the Coldbox conventions.

"... I want to set up my gatewayObject's readList() method to return a List object"

How does the readList() function return a list object if the returntype="void"?

"... then it creates a temporary object (which is an item bean) and passes the strTemp struct into the init() method to populate the bean."

I guess my main question is how to access the data in the populated bean?

Context: I have been trying to create an "edit existing list" page in the application (for practice and understanding), but I don't understand how to retrieve the values for the list object, in order to populate the form.

If you (or anyone) can provide a little tip on this aspect of Coldbox, I'd appreciate it.

GF
# Posted By Greg Franklin | 11/11/09 12:02 PM
Jason Dean's Gravatar @greg, great questions. These concepts are not ColdBox specific. These are concepts of both OO and CF.

First, notice that readList() does not return anything at the bottom. There is no <cfreturn /> in the function. But there is a <cfargument>. The method receives the bean that has already been created in our service layer as an argument. Objects in ColdFusion are passed by reference, which means that when we manipulate the passed in bean in the readList() method, it is actually making changes to the object that we already have control of in our service layer. So there is no need to return it. Technically, there is no reason that you could not change the return type to List and return the list with a <cfreturn> if it would make it easier to understand or read.

Once the data is in the bean (which happens when you pass the struct to the init() method of the bean) you can access that data using the bean's getter and setter methods. So if you want to get the list's title, you would call object.getTitle(), where "object" is the variable name that is storing the bean.

I hope this makes sense. These concepts can be very confusing, but they do apply across frameworks. Nothing in the explanation is ColdBox specific.
# Posted By Jason Dean | 11/11/09 1:27 PM
Rob Sherman's Gravatar Jason, great series! Just want to say that the ColdBox group on Google posted a link to a set of ColdBox Illudium PU-36 templates that should make this whole job easier. Here is that direct link:
http://www.coldbox.org/forgebox/view/Illudium-Scaf...
# Posted By Rob Sherman | 3/1/11 6:51 AM
Jason Dean's Gravatar Thanks Rob.
# Posted By Jason Dean | 3/1/11 11:57 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.1. Contact Blog Owner