ColdBox Directory Structure & Conventions

In order to create a ColdBox application you must adhere to some naming conventions and a directory structure. This is needed in order for ColdBox to find what it needs and to help you find modules more easily. It is up to you to organize your code. Also, ColdBox provides you with the ability to change these conventions for your applications. This is a great feature, since it lets developers create their own conventions on the internal structure of ColdBox Applications. The core conventions for a ColdBox Application are the following:

Core Conventions

The core conventions defined above are used to create a directory structure for your application and for some implicit executions. The conventions shown below are modeled in a directory structure in the next section.

  • config file location : Where the configuration file is located
  • handlers location : Where all the event handler cfc's exist
  • layouts location : Where all the layout templates exist
  • views location : Where all the views exist
  • plugins location : Where all the custom plugin cfc's exist
  • default event action : The name of the default event action to use.
  • models location : Where all your model objects exist

Directory Structure

Please note that the directory structure of your application relies on the conventions that you setup. So the structure changes according to the settings described above. Below is a typical directory layout:

+ApplicationRoot
|---+ config (REQUIRED where your coldbox.xml.cfm file goes or any other config files)
|---+ handlers (REQUIRED where your event handler cfc's go. You can use packages)
|---+ plugins (OPTIONAL where all your custom plugins go)
|---+ layouts (REQUIRED where all your layouts go)
|---+ views (REQUIRED where all your views go, you can use packages)
|---+ includes (OPTIONAL where your includes go:styles, images, etc. REQUIRED for i18N resource bundle locations.)
|---+ interceptors (OPTIONAL where your interceptors can go)
|---+ model (REQUIRED where all your model objects go)
|---+ logs (OPTIONAL where ColdBox can store the log files)
|---+ Application.cfc 
|---+ index.cfm

http://www.coldboxframework.com/includes/imagelibrary/ApplicationTemplate.png

Modifying Application Conventions

With the introduction of ColdBox 2.6, you can now setup an application's conventions via the configuration file. The only convention you can not directly modify in the configuration file is well, the location of the configuration file. However, you can still do this by setting up the location via the Application.cfc (See Loading a custom configuration file). These per-app conventions increase the portability of the ColdBox application, as now you can override any setting a framework-wide installation might have implemented and you are guaranteed that they will work. All you need to do is add the following to the coldbox.xml file:

<Conventions>
        <handlersLocation>_handlers</handlersLocation>
        <pluginsLocation>_myplugins</pluginsLocation>
        <layoutsLocation>_layouts</layoutsLocation>
        <viewsLocation>_views</viewsLocation>
        <eventAction>index</eventAction>
        <modelsLocation>_models</modelsLocation>                
</Conventions>
Important All conventions that are locations require a relative location to the Root of the running application. They cannot be set outside the root.

As you can see from the code above, you define the directory location for handlers, plugins, models, layouts and views. You can get creative with the names or event make them point to the same directory. The last element eventAction is not a location but an implicit execution action. This convention is explained in more detail in the following sections. In conclusion, that is all you need to do to tell the framework that you want to override the conventions and use the ones defined in your configuration file. Now just create the appropriate locations and you are ready to roll.

Modifying Framework-Wide Conventions

As you saw from the section above, you can have application conventions, but you can also define your own conventions for a single framework installation. This is useful to maintain integrity in development teams or if they are applicable to your applications. Custom app-conventions exist with portability in mind. There are two ways to modify the framework's conventions: ColdBox Dashboard or directly via the framework's settings.xml file. The ColdBox Dashboard provides an intuitive GUI to modify and maintain your ColdBox installation. So I strongly suggest using the Dashboard. If you would like to do this manually, you will first need to open the following file: coldbox/system/config/settings.xml. You will then see the following snippet and modify as needed. This will become your new application layout:

<!-- Conventions -->
<Conventions>
   <configLocation>config{sep}coldbox.xml.cfm,config{sep}config.xml.cfm</configLocation>
   <handlerLocation>handlers</handlerLocation>
   <layoutsLocation>layouts</layoutsLocation>
   <viewsLocation>views</viewsLocation>
   <pluginsLocation>plugins</pluginsLocation>
   <eventAction>index</eventAction>
   <modelsLocation>model</modelsLocation>
</Conventions>
IMPORTANT NOTE: The most important part of the convention snippet is the '''{sep}''' characters. This is IMPORTANT and NECESSARY. This is how the framework will replace your OS File Separator. So please be aware of it in the config location setting ONLY

Implicit Execution Conventions

ColdBox also provides you with several implicit execution conventions that are defined below. Some of these topics are touched in detail in other guides as well.

pre/post Handler events

Once you are familiar with event handlers and how events are created in ColdBox, you can now get a grasp on these two nifty implicit execution points. If you are not familiar with event handlers, please read the event handlers guide. You can create two methods in your event handler called:

  • preHandler()
  • postHandler()

As their prefix states the preHandler is an action that every time the framework needs to execute an event in that component, it will see if it has a preHandler method defined. If this method is defined, then it will execute before the event that was requested for execution. The postHandler if defined, will then be executed after the requested event got executed. Below is the cycle:

  1. Framework detects an event for execution and sends it for execution
  2. Framework checks if the event handler has a preHandler method
  3. Execute the preHandler method if found
  4. Execute the requested event
  5. Framework checks if the event handler has a postHandler method
  6. Execute the postHandler method if found

That's it, very simple, but incredibly useful.

<cffunction name="preHandler" access="public" returntype="void" output="false" hint="A preHandler action">
  <cfargument name="Event" type="any">
  <!--- Maybe check for event metadata and change layout --->
        
</cffunction>

<cffunction name="postHandler" access="public" returntype="void" output="false" hint="A postHandler action">
  <cfargument name="Event" type="any">
  <!--- Maybe do some AOP logging. --->
        
</cffunction>

Pre Handler Exception and Only Lists

The event handlers get some new public properties to use on its internal interceptor methods.

  • 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 get some new public properties to use on its internal interceptor methods.

  • 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">

Default Event Action

The default event action got introduced in version 2.6.0 and it allows for the execution of an action when only the handler has been defined in the incoming event or route. This is a great way to shorten urls and to have a convention of what to execute when no action has been determined for a specific event handler. The default event action is index. What this means is that you can create an action in your event handler cfc that looks like this:

<cffunction name="index" access="public" returntype="void" output="false" hint="The default action">
        <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>

Example: Let's say you have an event handler called users.cfc and you create the default event action on it called index(). So to call it you can do the following via the URL:

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

//route syntax
index.cfm/users

When the framework detects the incoming event, it will see that no action is defined and it matches a registered event handler cfc. It will then try to see if that event handler cfc has the default event action defined (index). If it does, it will then complete the event string to: users.index and execute the index action. That's it, very straightforward but simplicity is sometimes the best way.

onMissingAction

Again, the power of conventions. With this convention introduced in version 2.6.0, you can now create virtual events that do not even need to be created 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 and pass in an extra argument with the value of the requested action: missingAction. 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>
        //route or do appropriate logic for this.
        </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. The important thing to note is that actions can be defined via the url as event syntax or custom routes. With this feature you can create routes to such things as wiki/MyPageName that would map to a wiki handler and then the onMissingAction determine how to render MyPageName. Again, there are tons of possibilities for this feature.

Note: This convention will only fire if the onMissingAction method is defined in the event handler that got requested. If not, the normal error handling routines will fire.