Learning ColdBox: II
In Learning ColdBox: I, we began creating an app by getting ColdBox installed and responding. Today, we're going to go a bit deeper -- adding event handlers and associated events.
Let's stay with our simple requirement to get our application to produce "Hello, World" -- but with real events this time. I'll start by opening handlers/General.cfc. There's also a Main.cfc, that looks like it handles global implicit events such as:
- onAppInit
- onRequestStart
- onRequestEnd
- onSessionStart
- onSessionEnd
- onException
- onMissingTemplate
In my last post, we saw that config/Coldbox.cfc has a commented section called Implicit Events. I suspect that it's common practice to specify global implicit events as having handlers in the Main.cfc file -- though it looks like you can certainly place these handlers in whatever file you wish. Since convention is so helpful, both for remembering where you put that code as well as for working with other developers, I'm going with what I believe to be the flow on this.
Now, I could use the existing General.cfc event handler (aka controller), adding a new event (aka method), but I think I'll start with a new event handler/controller. Let's call it Philosopher.cfc. Eventually, this will house my Create/Read/Update/Delete (CRUD) events, but let's just get helloWorld working first.
One nice change to version 3 of ColdBox is the removal of the need to have my event controller extend the core ColdBox EventHandler. (You can still extend it if you wish, a la ColdBox 2.x) That's a nice touch -- one less thing to remember. So, here's my Philosopher.cfc, so far:
/**
* @output false
* @hint I provide CRUD functionality for philosophers
*/
component{}
Now, I'll create a method to handle the helloWorld event:
/**
* @output false
* @hint I greet the entire world
*/
public void function helloWorld( required struct event ) {
var rc = event.getCollection();
rc.welcomeMessage = 'Hello, World';
}
rc is shorthand for request collection. Here's what the always-excellent docs say about it:
ColdBox uses a data structure called the request collection to store the incoming FORM/URL/REMOTE variables and model a user's request. This collection lives inside of an object called Request Context. This object will become your best friend. A key important note about the collection is that this is how data gets moved around from event handlers to views and layouts to plugins and anything running inside of the framework.
Many frameworks work in this same way -- unifying all incoming variables into a single collection. ColdBox appears to use that request collection as a global, request-based resource -- a structure into which event handlers (and other exotic things that we'll eventually get to) can provide key/value pairs.
In this case, instead of hard-coding "Hello, World" (as I did with version I), I'll provide a welcomeMessage variable. (This could be called whatever I like; there's nothing special about that variable name.)
Now that I'm using the Model-View-Controller architecture (or at least the VC in MVC), I'll clean up my structure: I'll create a subdirectory called philosopher under views. That folder name maps exactly to my event handler, Philosopher.cfc. Into that new directory, I'll create a helloWorld.cfm page that looks like this:
<cfoutput>#rc.welcomeMessage#</cfoutput>
The variable, rc.welcomeMessage gets set in the controller and output in the view. Let's see if it works...

It does! We're using a little of ColdBox magic to include a view without specifying it. That works because of the mapping between Philosopher.helloWorld() and the view views/philosopher/helloworld.cfm. I know from working with Ruby on Rails (which works the same way) that this works great about 90% of the time. It just makes good sense to automate the way things work the vast majority of the time and allow you to manage the exception.
So, what if we do have an exception? Say I want to use world.cfm? ColdBox has a mechanism for this. too:
event.setView( 'philosopher/world');
And the world.cfm file:
<h4>world.cfm here...</h4><cfoutput> #rc.welcomeMessage# </cfoutput>
Which produces this:

OK! Although it's still early days, things are beginning to make sense. Or ColdBoxers -- do I have it wrong?
Code for version II: https://github.com/halhelms/ColdBox-Sample-App/branches/step2


// store all your form and other scopes in the rc var
var rc = event.getCollection();
// create your own private variables
prc = event.getCollection(private=true);
prc.message = "Hello World";
So now you have a cleaner separation of your own variables from those that ColdBox puts into the rc scope from a form submit or query string etc...
I only just learned about this the other day. Curt Gantz explained it a little on the ColdBox google group.
Hopefully my explanation makes sense.
A minor point but you say in the text "Say I want to use hello.cfm?", but your code says:
event.setView( 'philosopher/world');
I think you meant world.cfm.
The event argument passed into helloWorld is an object, so it should have a type of "any" or if you prefer "coldbox.system.beans.requestContext":
Also, as cfscript doesn't generate any whitespace (unless you're using writeoutput) you don't need all those pesky @output=false declarations. You don't even need to use the @hint demarcation as cf assumes that it is a hint if not defined.
You could also remove the "public" and "void" parts as those are the defaults if you want to reduce the amount of typing you have to do :)
So, if my choice of words confused you, sorry. If you'd like more explanation on how to use different view files, though, I'll be happy to pass my mustard seed of knowledge along to you.
public void function helloWorld( required coldbox.system.web.context.RequestContext event )