ColdBox's Event Handlers Guide

Introduction

This is a guide to event handlers for the ColdBox Coldfusion Framework. It will give you a quick overview of event handler syntax, regulations, locations, method invocations, and declarations. It will also show you some event handler code samples. Event handlers are synonymous to the word Controller in the MVC design pattern. So every time you hear event handler, you are taking about a controller.

What are Event Handlers?

ColdBox event handlers are cfc's (Coldfusion Components) that are written to implement ColdBox events that can be trigger externally via FORM/URL/REMOTE or internally by a developer. These events carry the task of controlling your application flow, calling business logic, preparing a display to a user and much more. Every method that has an access of public/remote is automatically exposed as a runnable event in the ColdBox framework and ColdBox will auto-registered for you. These events can then be defined for some explicit or implicit invocations via the coldbox.xml and from within the components themselves. Another important aspect of event-driven architectures is that the event action can be passed via the URL or the FORM, thus trigger an event in the system. The framework captures this action variable and uses it to execute the correct event handler method. From within the event handler's method, you will program the flow of the application, redirect data to the correct business logical units (model calls), logging, error trapping, validation, etc. If you have a Fusebox background you can relate to a handler cfc to being a circuit file and the methods in the handler to be like the fuseactions in the circuit file. However, with one MAJOR difference, you are coding in coldfusion and not in XML. There is no need for parsing or an XML dialect. Another major advantage is the benefit of using Coldbox Plugins from within your handlers, which provide you with several programming aspects that are already pre-built for you.

If you are not familiar with coldfusion components, you will have to get to speed in their usage in order to use the ColdBox framework. Below are some resources on Coldfusion Components:

Important Note: Event Handler's are not to be used to write business logic. They are used as controllers of your application, they make calls and redirect data. THAT IS IT!!! You can test code on them, but you cannot leave it there!!! ADHERE TO BEST PRACTICES!! If not this would not be an MVC framework.

How are events called?

As discussed in the introduction, events are determined via a special variable that can be sent in via the FORM or URL or REMOTELY. The default name for this variable is event. Of course, you can change this by updating the EventName setting in your configuration file. If no event name is detected as an incoming variable, the framework will look in the configuration settings for the DefaultEvent setting and use that instead. Ok, so now that we know how we can determine what event to execute, how do we write the events since they are used by convention?

All event handlers must be placed in the conventions directory of handlers in the application root. (See Directory Structure). At application startup, the framework registers all the valid event handler cfc's. So in order to call them you will use the following dot notation format:

{handler}.{method} = ColdBox Event
{package}.{handler}.{method} = ColdBox Event

This looks very similar to a java/cfc method call, example: String.getLength(), but without the parenthesis. Once the event variable is set and detected by the framework, the framework will tokenize the event string to retrieve the cfc and method call and validate it against the internal registry of registered events. It then continues to instantiate the event handler cfc or retrieve it from cache, and then finally executes the event handler's method.

Event url syntax can be done the regular way of index.cfm?event= or by using the ColdBox advanced SES routing system: index.cfm/handler/action. To learn more about routing, I suggest reading the SES pretty url guide AFTER this guide.

Examples:

//No Packages
index.cfm?event=main.index

//With SES routing
index.cfm/main/index

This will tell the framework too look for the main.cfc and execute the index() method.

//With Package
index.cfm?event=blog.main.entry

//With SES Routing
index.cfm/blog.main/entry

This will tell the framework too look for the blog directory and then for the main.cfc and execute the entry() method.

So just remember that when declaring event variables, they have to follow the dot notation.

Note: If you would like to alias your events so they are not binded directly to the handlers and methods, please use the included SES routing system with ColdBox. You can create routing and aliasing.

Implicit Declared Events

There are several events that can be declared implicitly in your configuration file. They are declared there because they correspond to several execution points in the life cycle of an application. Let's explore them:

  • Default Event : The default event to execute if no incoming event is detected
  • Request Start Handler : The event to execute at the beginning of every request
  • Request End Handler : The event to execute at the end of every request.
  • Session Start Handler : The event to execute at the beginning of a user's session
  • Session End Handler : The event to execute at the end of a user's session
  • Application Start Handler : The event to execute at the startup of the application
  • onInvalidEvent : The event to execute when the incoming event is invalid
  • onException : The event to execute when ANY exception is trapped within the framework.

I highly suggest also looking at the ColdBox Application Life Cycle Guide. To understand how they are fired. Also, please look at the application template's main.cfc handler. As it contains the skeleton of all of these events, as some of them have special variables in them.

Event Handlers Location

All event handlers should be placed in the handlers directory of your application. This is the location where ColdBox will look for event handlers, parse them and register them. However, you can also change this convention via the Dashboard or settings.xml file or configure the per-application conventions in your configuration file. All of the framework's conventions can be customized to your liking. Please see Naming Conventions Guide

|ApplicationRoot
|----+ handlers (Event  Handlers Directory

Event Handlers External Location

You can also declare a HandlersExternalLocation setting in your configuration file. This will be a dot notation path or instantiation path where more external event handlers can be found (You can use coldfusion mappings). Once declared, the framework will search that location for event handler cfc's and register them as external events. How will I call them then? The same way as normal events, using the syntax shown above. There is no distinction on your part, only for the framework. How cool is that?

Note: If an external event handler has the same name as an internal conventions event, the internal conventions event will take precedence.

Rules and Anatomy of an Event Handler

  • First of all, these cfc's must extend the coldbox eventhandler base cfc: coldbox.system.eventhandler
  • They must have an init method with the following standard IF and only IF you will be putting any initialization code for the entire event handler. If not, the init method is not mandatory.
  • They must exist in the correct handlers directory under your application. See Directory Structure or wherever you have pointed your custom conventions.
  • They must NOT contain any business logic, that is what the model or business layer is for, unless you don't want to.
  • They must determine what view will be rendered or what event to redirect execution to via a redirect or just implicitly giving execution to another event.
  • If the handlers are called via the ColdBox proxy from Flex/Air/Remote applications, your event handlers can actually return data by having a return type and returning a value.
  • They must have the public/remote exposed methods that will respond as external ColdBox events.
  • Private events have an access type of private and can only be called from within the application by using the runEvent() method.
  • Handlers are cached by default, unless the handler caching setting is off. You can configure persistence via metadata.
  • You can easily wire up dependencies in your handler from coldspring/lightwire or the coldbox cache by using the autowire interceptor.
Note: If a view is not set by an event handler, the rendering engine will throw an exception, unless a '''defaultView''' has been set in the coldbox.xml or the event.noRender() or the event.renderData() method has been used or the handlers is being called from the ColdBox Proxy.

Sample Handler Component Declaration

Below is a sample handler component declaration which can exhibit some caching parameters discussed below:

<cfcomponent name="security" hint="This is my security handler" extends="coldbox.system.eventhandler" cache="true" cachetimeout="20" cacheLastAccessTimeout="5">

  <!--- All my methods here --->

</cfcomponent>

The Caching Parameters

Since ColdBox is built with a solid cache foundation, your handlers can also be cached if needed. You will do this by adding meta data attributes to the cfcomponent tag. By default handlers WILL BE cached, unless you specifically use the cache meta data attributes to tell the framework NOT to cache it. Caching of handlers simulates persistence, so remember this if you are planning handlers that can maintain their own persistence and ALWAYS ALWAYS var scope your function variables. That is the number one reason for illusive errors and bad best practices.

ATTRIBUTE TYPE DESCRIPTION
cache boolean A true or false will let the framework know whether to cache this handler object or not.
cachetimeout numeric The timeout of the object in minutes. This is an optional attribute and if it is not used, the framework defaults to the default object timeout in the cache settings. You can place a 0 (Zero) in order to tell the framework to cache the handler for the entire application timeout controlled by coldfusion.
cacheLastAccesstimeout numeric The last access timeout of the object in minutes. This is an optional attribute and if it is not used, the framework defaults to the default last access object timeout in the cache settings. This tells the framework that if the object has not been accessed in X amount of minutes, then purge it.

Reserved Words and Methods

For a full list of reserved words and methods for handlers, please visit our Reserved Guide. I really recommend you read about this methods because that is how you interact with the framework. Some common methods are shown below:

  • announceInterception() : Announce a ColdBox interception
  • getColdboxOCM() : get a reference to the cache manager
  • getController() : get a reference to the coldbox controller
  • getDebugMode() : Whether you are in debug mode or not.
  • getfwLocale() : get the locale
  • getInterceptor() : Get an interceptor reference (only if loaded)
  • getMyPlugin() : Get a custom plugin
  • getPlugin() : Get a core or custom plugin
  • getResource() : Get a resource string from a resource bundle
  • getSetting() : Get a setting from your configuration file.
  • htmlhead() : Send text to the html head.
  • include() : Facade for cfinclude
  • dump() : facade for cfdump
  • abort() : facade for abort
  • includeUDF() : Inject a UDF into your handler
  • persistVariables() : Persist variables in the framework's flash ram
  • renderView() : Render View
  • runEvent() : Run an event
  • setNextEvent() : Relocate to the next event
  • setNextRoute() : Relocate to another route (SES)
  • setSetting() : set a new setting
  • settingExists() : Check if a setting exists
  • throw() : facade to cfthrow

Sample Init Method

The following is a sample init method that basically calls the super.init() method to initialize the handler and then continues to set some local properties for the handler. Remember that handlers ARE objects and have their own identity and persistence. As a best practice, ALWAYS create the init() method.

//Empty Init Method
<cffunction name="init" access="public" returntype="ehGeneral" output="false">
  <cfargument name="controller" type="any" required="yes">
  <cfscript>
  //Calling the the super init method is MANDATORY
  super.init(arguments.controller);
  //return instance
  return this;
  </cfscript>
</cffunction>

//Custom Init Method
<cffunction name="init" access="public" returntype="ehGeneral" output="false">
  <cfargument name="controller" type="any" required="yes">
  <cfscript>
  //Calling the the super init method is MANDATORY
  super.init(arguments.controller);
  
  //Any Code you like below
  setCreated(true);
  setWhatever( getPlugin('ioc').getBean('whatever') );
  
  //return instance
  return this;
  </cfscript>
</cffunction>

Anatomy of an Event Handler Method

As we discussed before a handler cfc contains several public/remote methods that can be executed by the framework. You can name these methods in any way you like as long as they make sense, naming is important for readability. With these methods you can call business logic, validate input, call plugins, etc. Well, so how do I do this? Each method will receive the request context object named Event as a parameter, which contains the request collection that we went over in the ColdBox overview guide and the request collection guide. If you have not read these guides, please do so now. This object contains the incoming request's data such as FORM/URL/REMOTE variables, the event requested, the view to render, the layout to render and any variables that your methods place in it. You can look at the CFC API to see all the methods that you have available in this object. The event object is the main object that you will be talking to in order to set/get data that can be shared in a request, set the layout or views to render, call plugins and more.

NOTE: The name of the argument received is Event of type coldbox.system.beans.requestContext

//public Event
<cffunction name="{Method Name}" access="public" returntype="void" output="false">
  <cfargument name="Event" type="coldbox.system.beans.requestContext">
</cffunction>

//private Event
<cffunction name="{Method Name}" access="private" returntype="void" output="false">
  <cfargument name="Event" type="coldbox.system.beans.requestContext">
</cffunction>

Event Caching

To learn more about event caching, please read the caching guide. In summary, event caching is the concept of caching what an event produces as HTML. For example, you have an event called blog.showEntry. This event executes, gets an entry from the database and sets a view to be rendered. The framework then renders the view and if event caching is turned on for this event, the framework will cache the HTML. So the next incoming show entry event will just spit out the cached html. Important to note also, that any combination of url parameters on an event will produce a unique cacheable entry. So event=blog.showEntry&id=1 & event=blog.showEntry&id=2 are two different events.

The way to set up an event for caching is on its cffunction declaration with the following extra arguments:

ATTRIBUTE TYPE DESCRIPTION
cache boolean A true or false will let the framework know whether to cache this event or not. The default is FALSE.
cachetimeout numeric The timeout of the event's output in minutes. This is an optional attribute and if it is not used, the framework defaults to the default object timeout in the cache settings. You can place a 0 in order to tell the framework to cache the event's output for the entire application timeout controlled by coldfusion, NOT GOOD. Always set a decent timeout for content.
cacheLastAccesstimeout numeric The last access timeout of the event's output in minutes. This is an optional attribute and if it is not used, the framework defaults to the default last access object timeout in the cache settings. This tells the framework that if the object has not been accessed in X amount of minutes, then purge it.

Now, for more information on how to use this and how to purge events, please read the Caching Guide.

//Sample event caching
<cffunction name="showEntry" access="public" returntype="void" output="false" cache="true" cacheTimeout="30" cacheLastAccessTimeout="15">
        <cfargument name="Event" type="coldbox.system.beans.requestContext">
        <cfscript>
                var rc = event.getCollection();
                
                //get Entry
                rc.entry = getEntryService().getEntry(event.getValue('entryID',0));
                
                //set view
                event.setView('showEntry');
        </cfscript>
</cffunction>


Note DO NOT cache events as unlimited timeouts. Also, all events can have an unlimited amount of permutations, so make sure they expire and you purge them constantly. Every event + URL/FORM variable combination will produce a new cacheable entry.

Method Samples

Can you show me an example of a method in real life? Sure, here are some examples from the ColdBoxReader application:

<cffunction name="index" access="public" returntype="void" output="false" cache="true" cacheTimeout="10">
        <cfargument name="Event" type="coldbox.system.beans.requestContext">
        
        <!--- Reference to the request collection --->
        <cfset var rc = event.getCollection();
        
        <!--- set a variable in the request collection --->
        <cfset rc.myName = "Luiggi Majano">
        
        <!--- Set the view to render --->
        <cfset event.setView('home')>
</cffunction>


<cffunction name="onException" access="public" returntype="void" output="false">
        <cfargument name="Event" type="coldbox.system.beans.requestContext">
        <!--- My own Exception Handler --->
        <!--- Log error --->
        <cfset var exceptionBean = Event.getValue("ExceptionBean")>
        
        <!--- Do per Type Validations, example here --->
        <cfif exceptionBean.getType eq "Framework.SettingNotFoundException">
                <cfset getPlugin("messagebox").setMessage("warning", "Settings not found by my funky code.")>
                <!--- Relocate to default event, by not passing an event to the setnextevent method --->
                <cfset setNextEvent()>
        <cfelse>
                <!--- Else, just log the errors and finalize execution, the framework then presents an error page--->
                <cfset getPlugin("logger").logErrorWithBean(exceptionBean)>
        </cfif>
</cffunction>


<cffunction name="doCreateAccount" access="public" returntype="void" output="false">
        <cfargument name="Event" type="coldbox.system.beans.requestContext">
        <cfscript>
        var password2 = Event.getValue("password2","");
        var userService = getPlugin("ioc").getBean("userService");
        var userBean = userService.createUserBean();
        //Get a reference to the request collection for shorter typing, I am lazy.
        var rc = Event.getCollection();
        //Populate Bean From Request Collection via the beanfactory plugin
        userBean = getPlugin("beanFactory").populateBean(userBean);
                        
        if ( userBean.getUserName() eq "" or userBean.getPassword() eq "" or userBean.getemail() eq ""){
                getPlugin("messagebox").setMessage("warning", "Please enter all the account information in order to create an account.");
                setNextEvent("ehUser.dspSignUp");
        }
        if ( compare(UserBean.getpassword(),password2) neq 0 ){
                getPlugin("messagebox").setMessage("warning", "The passwords do not match.");
                setNextEvent("ehUser.dspSignup");
        }
        try {
                userService.saveUser(userBean);
                userBean.setVerified(true);
                //set session object
                getPlugin("sessionstorage").set("oUserBean",userBean);
        
                //relocate
                setNextEvent("ehGeneral.dspReader");
        } catch (any e) {
                getPlugin("messagebox").setMessage("error", e.message & "<br>" & e.detail);
                //Call internal event by passing the request context
                dspSignUp(Event);
                //or you can use the runEvent method
                //runEvent("ehUser.dspSignUp")
        }
        </cfscript>
</cffunction>

How to set and get values (Event Handlers, Views, Layouts)

In order for any event handler to work, it needs values. Most likely url parameters, form submissions or application/session/client variables. The framework provides you with a request collection structure for all your variable needs, modeled after the requestContext object. This object gets passed in to every event handler method with the argument name of event and type coldbox.system.beans.requestContext. The object also gets prepared for usage in your views and layouts as event and a reference to the request collection as a scope named rc. Below is an example of a event handler method using the request collection.

<cffunction name="dspHome" access="public" returntype="void" output="false">
  <cfargument name="Event" type="coldbox.system.beans.requestContext">
  <!--- Do Your Logic Here to prepare a view --->
  <cfset Event.setValue("welcomeMessage","Welcome to ColdBox!")>        
  <!--- Set the View To Display, after Logic --->
  <cfset Event.setView("home")>
</cffunction>
  1. getValue ( name, defaultValue )
    1. name: The name of the variable to return from the collection
    2. defaultValue: The default variable to return if the variable is not found in the collection. Since there are no default values that can be set for complex variables, you can send the following action keywords to return an empty complex variable according to the keyword. Please note that you need to pass in the keyword in brackets. Else the regular expression match will fail and the simple value will be returned.
      1. [array]
      2. [struct]
      3. [query]
  2. setValue ( name, value )
    1. name: The name of the variable to set in the reqCollection.
    2. value: The value of the variable (simple or complex)
  3. getCollection() : Returns the entire request collection data structure. Very useful to send in to the business layers or create a reference scope.
  4. paramValue(name, value) : This method checks if the name exists in the collection, if not, then it creates it with the value.
  5. removeValue(name) : Remove a value from the collection
  6. valueExists(name) : Checks if the value exists in the collection.
<cfscript>
//Creating a reference scope
var rc = event.getCollection();

//set a value
event.setValue("name", "Luis");

//param a value
event.paramValue("user_id","");

//remove a value
event.removeValue("name");

//check if value exists
if( event.valueExists("name") ){

}
</cfscript>

So if you need to retrieve a value from a form POST or URL parameters, then you will use the event.getValue() method. If you need to store values into the collection in order for the views and layouts to access them, use the event.setValue() method. Pleas view the API to learn more about the event object.

How to relocate to another event

The framework provides you with two methods that you can use to relocate to other events, 1 for both normal and ses urls and another for only ses urls:

  • setNextEvent([string event], [string queryString], [boolean addToken], [string persist], [struct varStruct], [boolean ssl],[string baseURL])
  • setNextRoute(string route, [string persist], [struct varStruct],[boolean addToken],[boolean ssl])

setNextEvent

The setNextEvent method can be used for both normal and ses urls, here are its parameters:

Argument Required Type Description
event false string The event to relocate to, if empty it defaults to the default event. ex: main.home
queryString false string The query string to append to the relocation
addToken false boolean Whether to add the cf tokens or not. Default is false
persist false string(list) A comma-delimited list of request collection key names that will be flash persisted in the framework's flash RAM and re-inflated in the next request.
varStruct false struct A structure of key-value pairs that will be flash persisted in the framework's flash RAM and re-inflated in the next request.
ssl false boolean(false) Flag indicating if redirect should be done in ssl mode or not
baseURL false string If used, then it is the base url for normal syntax redirection instead of just redirecting to the index.cfm

All the arguments above are pretty straightforward except: persist & varStruct. Persist can be a comma-delimited list of request collection' key names that you want to persist in the internal flash memory of the framework for the relocation.varStruct is a structure of key-value pairs that will be persisted across a request. So when the user get's relocated, those variables (can be simple, complex, or even objects) will be flash stored and re-inflated back to the request collection on the relocation. Very useful to silently keep state on temporary objects.

Important: Please note that the persist argument refers to items ALREADY in the request collection.

setNextRoute

The setNextRoute method can be used for ses urls and it can use the following arguments:

Argument Required Type Description
route true string The route to relocate to. ex: main/home
persist false string(list) A comma-delimited list of request collection key names to persist in the relocation
varStruct false struct A structure of key-value pairs that will be flash persisted in the framework's flash RAM and re-inflated in the next request.
addToken false boolean(false) Flag to add the CF tokens to the redirection
ssl false boolean(false) Flag indicating if redirect should be done in ssl mode or not

In order to understand routes in ColdBox, please refer to the SES and Pretty URL guide. It will explain how you can use these incredible routing system.

Setting Views

The event object is the object that will let you set the views that you want to render, so please explore its api and the request collection guide. To quickly set a view to render, do the following:

<cfset event.setView('view')>

The view name is the name of the template in the views directory without appending the .cfm. So if the view is inside another directory you would do this:

<cfset event.setView('mydirectory/myView')>

You can also do caching of views, for that read the caching guide.

What if I don't want to render anything?

Well, if you don't want to, then you don't have to. The framework gives you a method in the event object that you can use if maybe this specific request should just terminate gracefully and not render anything at all. All you need to do is use the event object to call on the noRender() method.

<cfset event.noRender()>

This method tells the framework that this request will not produce any output, so just finalize the request. Most likely you will end up with a white page or if called from ajax, nothing.

Rendering Data

You can also use the event.renderData() method to render and marshal data directly from an event handler without the need to set a view for rendering. You do this via the renderData() method in the event object and you can read all about it in the Ajax Guide?.

//render a structure as JSON
<cfset event.renderData(type='JSON', data=myStruct)>

//render as WDDX
<cfset event.renderData(type='WDDX', data=myStruct)>

//render plain data.
<cfset event.renderData(type='plain', data="Hello")>

Event Default Action

The event default action setup by the framework is index. What this means, is that if the framework detects that an incoming event has no action attached to it, it should look for a method in that handler called index. If it exists, then it will execute it as the incoming event. You can change the name of this default action in your configuration file or via the settings.xml file. In future versions, you will be able to define the default action that can be specific to each handler, as of now, its application wide. So for example, if we have an event handler called users and we call it like so:

index.cfm?event=users
or
index.cfm/users

The framework will look in the users handler for the index method. If it finds it, then it will treat the request as users.index. This is a very nice cool feature that can let you do some implicit declarations.

<cffunction name="index" returntype="void" access="public">
  <cfargument name="event">
  
</cffunction>

Event Handler Implicit Events: preHandler, postHandler

There are also two implicit events that can be declared in your event handler that the framework will use in order to execute them anytime an event is fired from the current handler. This is great for intercepting calls and for pre/post processing before any type of event. If you declared them, the framework will execute them. (See Request Life Cycle)

preHandler Executes before the requested event (In the same handler cfc)
postHandler Executes after the requested event (In the same handler cfc)

Example:

<cffunction name="preHandler" output="false" returntype="void" access="public">
  <cfargument name="Event" type="coldbox.system.beans.requestContext">
  <cfscript>
  //Execute any pre-event code here. Like AOP logging, etc.
  
  </cfscript>
</cffunction>

<cffunction name="postHandler" output="false" returntype="void" access="public">
  <cfargument name="Event" type="coldbox.system.beans.requestContext">
  <cfscript>
  //Execute any post-event code here. Like layout considerations, etc.
  
  event.setLayout('Layout.PDF');
  </cfscript>
</cffunction>

Pre Handler Exception and Only Lists

The event handlers have some public properties that can be used to modify the preHandler interception method.

  • You can define an exception list of actions for the event handler's preHandler() method by using the this.prehandler_exception property.
  • You can define an execution only list of actions for the event handler's preHandler() method by using the this.prehandler_only property.
<!--- Execute the prehandler() method only for the private action --->
<cfset this.prehandler_only = "private">

<!--- Do not execute the preHandler() method for the login,logout and dspLogin actions --->
<cfset this.prehandler_exception = "login,logout,dspLogin">

Post Handler Exception and Only Lists

The event handlers have some public properties that can be used to modify the postHandler interception method.

  • You can define an exception list of actions for the event handler's postHandler() method by using the this.posthandler_exception property.
  • You can define an execution only list of actions for the event handler's postHandler() method by using the this.posthandler_only property.
<!--- Execute the posthandler() method only for the private action --->
<cfset this.posthandler_only = "private">

<!--- Do not execute the posthandler() method for the login,logout and dspLogin actions --->
<cfset this.posthandler_exception = "login,logout,dspLogin">

onMissingAction convention

Again, the power of conventions. With this convention you can now create virtual events that do not even need to be created or exist in a handler. Every time an event requests an action from an event handler and that action does not exists in the handler, the framework will check if an onMissingAction() event action has been declared. If it has, it will execute it. This is very similar to ColdFusion's onMissingMethod but on an event-driven framework.

Below is the skeleton for such an event action:

<!--- onMissingAction --->
<cffunction name="onMissingAction" access="public" returntype="void" output="false" hint="on missing action">
    <cfargument name="Event" type="coldbox.system.beans.requestContext" required="yes">
    <cfargument name="missingAction" required="true" type="string" hint="">
    <cfset var rc = event.getCollection()>
        <cfscript>
                
        </cfscript> 
</cffunction>

This event has an extra argument: missingAction which is the missing action that was requested. You can then do any kind of logic against this missing action and decide to do internal processing, error handling or anything you like. The power of this convention method is extraordinary, you have tons of possibilities as you can create virtual events on specific event handlers. A good sample is for example a wiki processing page. You can have a route declared like this:

/wiki/page_name

Then have a wiki handler with the onMissingAction() method. Every time a page is requested, you can execute the onMissingAction() and render the correct page.

Note: This convention will only fire if the missing action is on a specified event handler and the onMissingAction method exists on the handler. If not, the normal error handling routines will fire.

Handler Public Property: this.EVENT_CACHE_SUFFIX

This property is great for adding your own dynamic suffixes when using event caching. All you need to do is create a public property called EVENT_CACHE_SUFFIX and populate it with something you want. Then the event caching mechanisms will automatically append the suffix and thus create event caching using this suffix for the entire handler.

<cfset this.EVENT_CACHE_SUFFIX = "My Suffix>
Note: This suffix will be appended to ALL events that are marked for caching within the handler in question ONLY.

Persisting Flash Variables

As we shown before, you can use ColdBox's flash RAM persistence via the setNextRoute or setNextEvent methods. However, you have a third method that you can use to persist variables on-demand, persistVariables(). This method not only persists variables when you call it, but you can call it multiple times in a request and it will keep appending to the persistence struct (Just be careful not to override stuff).

  • persistVariables([string persist], [struct varStruct])

The two arguments can be by themselves or together if needed. The persist argument is a comma delimmitted list of keys of variables in the request collection to persist. Please note this, you pass in a list of keys that MUST exist in the request collection in order to exist. The varStruct argument is a structure of key-value pairs that you would like to append to the persistence structure. You can set variables anywhere you want, add it to this structure and then persist it.

rc.user_id = oUser.getUserid();
rc.salt = createUUID();
persistVariables(persist="user_id,salt");


myStruct = {user_id=oUser.getUserID(), salt=createUUID(), logindate=now()};
persistVariables(varStruct=myStruct);

Executing Events

Apart from executing events from the URL/FORM or Remote interfaces, you can also execute events, either public or private from within your event handlers. You do this by using the runEvent() method:

  • runEvent([string event], [boolean prepostExempt], [boolean private])

Arguments

argument type required default description
event string true --- The event to execute.
prepostExempt boolean false false If set to true, it will bypass any pre or post handler implicit executions.
private boolean false false If set to true, it will try to execute a private event.

As you can see, you can execute events on demand with this method:

//public event
<cfset runEvent('users.save')>

//post exempt
<cfset runEvent(event='users.save',prepostExemp=true)>

//Private event
<cfset runEvent(event='users.persist',private=true)>

Autowiring Your Handlers

You can easily autowire your handlers with objects coming from the IoC plugin or the coldbox cache by using the autowire interceptor. For a full hands on guide, please read the Autowire Guide as it will show you how to wire up your handlers with dependencies.

Best Practices

Organization

I always try to have some kind of mapping between the different logical sections or modules of my application and their event handlers. For example, if my application has a section for user management, with master and detail views, I'd create a single event handler cfc to hold all the methods related to that module. Now, large sections of your application that are more complex and have lots of actions and views, may require you to split the event handlers even more (like packages/directories), but the idea is the same.

The handler cfc's are also objects and therefore they should have their specific identity and function. So you need to map them in the best logical way according to their functions. If you need more flexibility you can even create directories (packages) and place them in logical buckets. This is mostly used in large applications as mentioned above. So think of them as entities and how they would do tasks for you via the events. Once you do this, you can come up with a very good cohesive event model for your applications. The application template creates two event handlers for you: main and general. The main contains mostly implicit methods for use in conjunction with the configuration file: coldbox.xml. The general is a general event handler and so forth.

In conclusion, organize your handlers as you would a domain model, put in practice your Ontology skills and define what these handlers will do for your, what is their identity.

Executing other events (Event Chaining)

The best practice on event execution would be via the runEvent() method. That is what is there for and it should be used. You can bypass it when calling methods in the same event handler. However, the best practice would be to use the provided method.

Init Method

Every handler does NOT need an init method, but if used, it must comply with the standard format shown in this guide. In that init method you can do whatever you want. If you want to set private properties to a handler, due composition of services, etc. You can.

Naming Conventions

Try always to add meaningful names to methods so other developers and users can understand what you are doing.

Advanced OO Features: UDF Injections

ColdBox provides you with a way to actually inject your event handlers with custom UDF's so you can change the behavior or expand on the behavior an event handler already has. This is called mixin methods and can be done via the includeUDF() method provided to every event handler or via the UDFLibrary setting in your coldbox.xml. The method is a provided way for you to dynamically load UDF's into your event handlers at runtime. You can do this in a specific method or at the initialization of the handler via the init() method. The sample below is using the init method:

<cffunction name="init" access="public" returntype="ehGeneral" output="false">
  <cfargument name="controller" type="any" required="yes">
  <cfscript>
  super.init(arguments.controller);
  //Any Code you like below
  
  //Load my special UDF's for my tools handler.
  includeUDF('includes/toolsUDF.cfm');
  
  //return instance.
  return this;
  </cfscript>
</cffunction>   
        

The includeUDF method call will find the template and inject it to the handler.

WARNING: If you try to inject a method that already exists in the handler, the call will fail and !ColdFusion will throw an exception.

There you go, that is dynamic loading of methods in your handlers. If you choose the route of the config file, you will declare the location of the UDF template to load into the handlers, layouts and views. That is the main difference between calling the includeUDF method and using the config setting.