ColdBox Basic – Event Handlers


ColdBox event handlers are the controllers in the MVC design pattern. They are ColdFusion components which are responsible for handling requests coming into the application from either a FORM, URL, REST or remote sources (Flex/Air/SOAP). These event handlers carry the task of controlling your application flow, calling business logic, and preparing a display to a user.

Environment

  • ColdBox 3.8
  • Windows OS

ColdBox Framework Request Context

  • The incoming URL, form and Remote variables are merged into a single structure called the request collection (i.e rc) structure. This structure resides inside the request context object.
  • Internally, a second collection called the private request collection (i.e. prc) is created to store data and objects that have no outside effect.

Event Handlers Location and Calling

1. Location

  • All event handlers should be saved in the handlers folder
  • For large applications, it is best to create packages or sub-folders under the handlers folder for better maintenance and URL experience
  • Can also declare a HandlersExternalLocation setting in the ConfigurationCFC (ColdBox.cfc)

2. Calling Events

Without SES routing, use the following event syntax notation format:

  • no event : Default event by convention is main.index
  • event={handler} : Default action method by convention is index()
  • event={handler}.{method} : Explicit handler + action method
  • event={package}.{handler}.{method} : Packaged notation
  • event={module}:{package}.{handler}.{method} : Module Notation

With SES routing, use the following event syntax notation format:

  • no event : Default event by convention is main.index
  • {handler} : Default action method by convention is index()
  • {handler}/{method} : Explicit handler + action method
  • {package}.{handler}/{method} : Packaged notation
  • {module}:{package}.{handler}/{method} : Module Notation

Here are a few simple examples to illustrate:

//No Packages
index.cfm?event=clients.list

//With SES routing
index.cfm/clients/list

component name="clients" {
   function list(event,rc,prc){
      return "I am the client handler's list action!";
   }
}

//With 'public' Package
index.cfm?event=public.clients.list

//With SES Routing
index.cfm/public.clients/list

OK. Now that we know something about ColdBox event handlers, let’s create an event handler for the client model and some of its actions:

  • list
  • add/edit
  • save
  • remove

Create the Handler

In the handlers folder, create a new ColdFusion component and save it as Clients.cfc. Edit this new component and add the following skeleton code:

component accessors="true" {
   //dependency injection
   property name="clientService" inject="model:ClientService";

   function preHandler(event,action){
      //preHandler stuff goes here
   }

   public void function index(){
      //this is the default action
      setNextEvent("clients/list");
   }
}//component

Create the List Action

This handler return all instances from the client model. Here’s a code sample to illustrate:

public void function list(event){
   var rc = event.getCollection();
   event.paramValue("client_id","");

   rc.clients = clientService.list(sortOrder="lastName desc",asQuery=false);
} //list

Notes:

  • On line 2, we use the getCollection function to retrieve the request collection. It is also possible to just pass in the request collection as an argument to the action function, i.e. function list(event,rc,prc). In that case, line 2 won’t be necessary.
  • On line 3, we use event.paramValue to specify a default value for client_id. This is similar to the cfparam tag
  • On line 5, we are using the Base ORM service list function to return all instances of the client entity (i.e. all records in the clients table).

Create the Add/Edit Action

In this handler action, we are retrieving a particular record by its primary identifier, usually the primary key, to add or edit.

public void function edit(event,rc,prc){
   rc.client = clientService.get( event.getValue("client_id","") );
} //edit

Notes

  • On line 2 we use the Base ORM Service get function to retrieve a particular instance from the client model
  • Also on line 2 we use event.getValue to set a default value for client_id if it doesn’t exists
  • If client_id is blank than it is an add action, otherwise we are performing an edit

Create the Save Action

public void function save(event,rc,prc){
   var client= populateModel( clientService.get(event.getValue("client_id","") ));
   var vResults = validateModel(client);
   
   if( !isNull(client) ) { 
      // validate model       
      if( vResults.hasErrors() ){ 
         getPlugin("MessageBox").error( messageArray=vResults.getAllErrors()); 
         setNextEvent("clients.edit/" & rc.client_id); 
      } 
      else { 
         // save the model
         clientService.save(client); 
         getPlugin("messagebox").setMessage("info","Client #rc.firstname# #rc.lastname# was successfully #rc.context#.");
         setNextEvent("clients.list");
      } 
      else { 
         getPlugin("messagebox").setMessage("info","Client was not found."); 
      }
}//save 

Notes

  • On line 2 we use the PopulateModel function to populate the client model using data from the request collection by matching the form element namw to the model property
  • So for example if the form has an input element name firstname, and the model has a property called firstname, then the values of firstname from the form scope will be placed in the model’s firstname property
  • It is also possible to use each property’s setter to set the property value if you have form element names that don’t match with the model property name, e.g. client.SetFirstname(rc.first_name)
  • Hence it is important that the form element name matches the model property if you want to use PopulateModel function
  • On line 3 we use the validateModel function to perform validation based on the constraints defined for the model. If it fails, we will display a message for the user to fix before proceeding to save the model
  • On line 8 we display the error to the user if the model validation fails using the MessageBox plugin
  • On line 13 the model validation succeeds so we proceed to save the model (i.e. write the update to the database) using the Base ORM service save function
  • On line 14 we redirect to the client listing page after the save was successful using the setNextEvent function
  • rc.context is set on the edit view and signifies whether it is an add or edit action. This is done by checking to see whether client_id is blank. If it is, then it’s an add action, so rc.context stores the value of added. Otherwise it is set to saved.

Create the Remove Action

public void function remove(event,rc,prc){
   var client = clientService.get( event.getValue("client_id","") );
   
   if (event.getHTTPMethod() == "GET") {
      getPlugin("messagebox").setMessage("error","Invalid action!");
   }
   else {
      if( !isNull(client)){
         clientService.delete(client);
         getPlugin("messagebox").setMessage("info","#client.getFirstname()# #client.getLastname()# was successfully deleted.");
      }
      else {
         getPlugin("messagebox").setMessage("error","We are unable to locate the client you are trying to remove, please try again.");
      }
   }
   setNextEvent("clients.list");
} //remove

Notes

  • On line 2 we retrieve the instance we want to delete using the Base ORM service get function and the primary key, i.e. client_id
  • On line 4 we check to ensure that it is an HTTP POST method to prevent unauthorised use of HTTP methods like GET
  • On line 9 we remove this instance from the model using the Base ORM service delete function

The Full Handler Code

The final code for the handler Clients.cfc should look like this:

component accessors="true" {
   /* handlers/Clients.cfc */
   property name="clientService" inject="model:ClientService";

   function preHandler(event,action){

   }

   public void function index(){
      setNextEvent("clients/list");
   }

   public void function list(event){
      var rc = event.getCollection();
      event.paramValue("client_id","");

      rc.clients = clientService.list(sortOrder="lastName desc",asQuery=false);
   } //list

   public void function edit(event,rc,prc){
      rc.client = clientService.get( event.getValue("client_id","") );
   } //edit

   public void function save(event,rc,prc){
      var client= populateModel( clientService.get(event.getValue("client_id","") ));
      var vResults = validateModel(client);
   
      if( !isNull(client) ) { 
         // validate model       
         if( vResults.hasErrors() ){ 
            getPlugin("MessageBox").error( messageArray=vResults.getAllErrors()); 
            setNextEvent("clients.edit/" & rc.client_id); 
         } 
         else { 
            // save the model
            clientService.save(client); 
            getPlugin("messagebox").setMessage("info","Client #rc.firstname# #rc.lastname# was successfully #rc.context#.");
            setNextEvent("clients.list");
         } 
      else { 
         getPlugin("messagebox").setMessage("info","Client was not found."); 
      }
    }//save

    public void function remove(event,rc,prc){
      var client = clientService.get( event.getValue("client_id","") );
      
      if (event.getHTTPMethod == "GET"){
         getPlugin("messagebox").setMessage("error","Invalid action!");
      }
      else {
         if( !isNull(client)){
            clientService.delete(client);
            getPlugin("messagebox").setMessage("info","#client.getFirstname()# #client.getLastname()# was successfully deleted.");
         }
         else {
            getPlugin("messagebox").setMessage("error","We are unable to locate the client you are trying to remove, please try again.");
         }
      }
      setNextEvent("clients.list");
   } //remove
 }//component

Further Information

For more detailed information on ColdBox’s Base ORM Service, PopulateModel and ValidateModel functions, dependency injection and event handlers, please click on one of the following links:

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s