Learning ColdBox: IV
Over the last couple of weeks since starting this "Learning ColdBox" series, I've been working more extensively with the framework. There's a lot in ColdBox and it took me quite a while to really feel comfortable with it. Having gotten over that initial hurdle, I'm very glad I did. Many thanks to my colleague, Ben Densmore, who has been always willing to answer my naive ColdBox questions -- even at 2.00 AM. I'm by no means an expert; I've just gotten to the point where I'm happy being a bit reckless!
So, to get us back up to speed, we're going to build a little game app that will allow players to let philosophers duke it out for supremacy. First, though, we need to get the philosophers into the system. And that means we need to create functionality to create, read, update and delete philosophers.
Inside the handlers directory, I've created a Philosopher CFC. ColdBox is a Model-View-Controller (MVC) framework and the handlers represent the controllers. Let's take a look, then, at my controller for dealing with CRUD functionality for philosophers:
- list: a listing of all philosophers
- modelForm: a form for creating a new or editing an existing philosopher
- show: a simple output of the properties of a philosopher
- save: a creation/updating and persistence method
- delete: a delete method
Here's the code:
/**
* @output false
* @hint I am the CRUD controller for philosophers.
*/
component{
property name="orm" inject="entityservice:philosopher";
/**
* I am a no-args constructor.
*/
public Philosopher function init() {
return this;
}
/**
* I provide a listing of all philosophers.
*/
public void function list( event ) {
var prc = event.getCollection( private=true );
prc.philosophers = variables.orm.getAll();
}
/**
* I provide a form for creating/editing a philosopher.
*/
public void function modelForm( event ) {
var prc = event.getCollection( private=true );
prc.philosopher = variables.orm.get( prc.philosopherId );
}
/**
* I provide a view into a philosopher.
*/
public void function show( event ) {
var prc = event.getCollection( private=true );
prc.philosopher = variables.orm.get( prc.philosopherId );
}
/**
* I handle the actual creation/updating and persisting of a new philosopher.
*/
public void function save( event ) {
var prc = event.getCollection( private=true );
var philosopher = variables.orm.get( prc.philosopherId );
populateModel( philosopher );
variables.orm.save( philosopher );
setNextEvent( event='philosopher.show', queryString='id=#philosopher.getId()#');
}
/**
* I delete a philosopher, given a legitimate philosopher ID.
*/
public void function delete ( event ) {
var prc = event.getCollection( private=true );
var philosopher = variables.orm.get( rc.philosopherId );
variables.orm.delete( philosopher );
setNextEvent();
}
}
Probably the only part of this that needs some explanation is this line:
property name="orm" inject="entityservice:philosopher";
This code puts an Object-Relational Manager in the variables scope. It uses Adobe's implementation of Hibernate. I've said before that when I heard Adobe was going to attempt to implement Hibernate, I was worried about all the ways that could go wrong. Luckily, I was dead wrong; it's quite good. Kudos to Adobe.
The ORM ColdBox provides makes working with persistence even simpler. With entityservice:philosopher, I get an ORM service already tied to the Philosopher model.
Here, then, are some quick notes on the various controller actions:
list: All of the actions create a prc variable. The event has lots of information stored in it. (One example: all of the form and/or URL variables are placed into the event.) Calling the getCollection method returns a structure of these various name/value pairs. Adding private=true to the call copies them into a separate structure (to prevent overriding the initial values). At least, I'm pretty sure that's what it does. ColdBoxers, let me know if I have this wrong.
UPDATE No, actually, adding the private=true to the getCollection getCollection call doesn't duplicate the struct. At the risk of being wrong twice in the same post about the same subject(!), I'm guessing it's a completely separate structure. I don't get why one would use the same getCollection method when it appears not to get the collection is confusing. There may be a very good reason for it, of course. I'm hoping more experienced ColdBoxers will set me straight on this.
Within the prc, I'm calling the entity service, asking for a collection of all philosophers that have been saved to the database. As with all of the actions, I don't need to specify a view; I'm relying on ColdBox's assumption that, unless otherwise stated, the view file will be named the same as the action (with a .cfm extension). So the list action assumes a file, /views/list.cfm.
modelForm: Before posting this, I showed Luis Majano my code, asking for a quick sanity check. In the code I sent him, I had a create and and update method. Luis informed me that I could combine those into one (save) since the get method, if not passed an ID, would return a new instance of the Philosopher object. This is a very nice feature -- showing the kind of thoughtful care that characterizes ColdBox. Not only was I able to combine create and update, but I was able to collapse newForm and editForm into a single method.
save: Notice the call to populateModel. This uses the data found in the event to attempt to populate a model. (Obviously, variable names in the event will have to match the model's property names.) Another very nice feature of ColdBox.
Since I have no file I wish to display, calling setNextEvent() moves the action along without attempting to display a view file. This is also used in the delete method.
Let's stop for now. When I pick it up again, we'll take a look at the view and model parts of this MVC framework.


The PRC (Private Request Collection) lives alongside the RC in the event object. However, this structure is NOT affected by an outside force like FORM/URL/REMOTE. This means that nobody from outside world can influence values in this collection.