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 TodoGatewayFirst, 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">
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 weirdNow 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 CompositionLet'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() />
<cfquery name="qRead" datasource="#variables.dsn#">
SELECT listid, name, priority
WHERE listid = <cfqueryparam value="#arguments.list.getlistid()#" CFSQLType="cf_sql_integer" />
<!--- leave the bean as is and set an empty query for the conditional logic below --->
<cfset qRead = queryNew("id") />
<cfset strReturn = queryRowToStruct(qRead)>
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 ObjectWell, 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 = "" />
<cfquery name="qItems" datasource="#variables.dsn#">
SELECT itemid, title, dueDate
WHERE listid_fk = <cfqueryparam value="#arguments.id#" cfsqltype="cf_sql_integer" />
<cfset qItems = QueryNew("id") />
<cfreturn qItems />
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()) />
<cfset strTemp = queryRowToStruct(qItems, currentrow) />
<cfset tmpObj = createObject("component","model.todos.item").init(argumentCollection=strTemp) />
<cfset arguments.list.addItem(tmpObject) />
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) />
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.