Thursday, February 17, 2011

ColdFusion ORM: Answering Some Event Handling Quandaries

I'm working with ColdFusion ORM event handlers and had a few questions I couldn't find answers to so I figured I'd write some tests.  I wanted to determine:
  1. Does preUpdate fire if no properties have changed?
  2. Does postUpdate fire if no properties have changed?
  3. What is the order of operations when using both a CFC event handler and object event handler method?
I figured the best way to test this would be to persist a bunch of tick counts during each event handlers process.  The persistent CFC is as follows:

<cfcomponent displayname="EventRecorder" persistent="true">
  
  <!--- properties --->
  <cfproperty name="id" fieldtype="id" type="numeric" unsavedvalue="-1" generator="increment" />
  <cfproperty name="preInsertTick" type="int" />
  <cfproperty name="postInsertTick" type="int" />
  <cfproperty name="preUpdateTick" type="int" />
  <cfproperty name="postUpdateTick" type="int" />
  <cfproperty name="sysPreInsertTick" type="int" />
  <cfproperty name="sysPostInsertTick" type="int" />
  <cfproperty name="sysPreUpdateTick" type="int" />
  <cfproperty name="sysPostUpdateTick" type="int" />
  
  <cffunction name="init" returntype="EventRecorder" output="false">
    <cfset setID(-1) />
    <cfreturn this />
  </cffunction>
  
  <cffunction name="preInsert" returntype="void" output="false">
    <cfset setPreInsertTick(getTickCount()) />
  </cffunction>
  
  <cffunction name="postInsert" returntype="void" output="false">
    <cfset setPostInsertTick(getTickCount()) />
  </cffunction>
  
  <cffunction name="preUpdate" returntype="void" output="false">
    <cfset setPreUpdateTick(getTickCount()) />
  </cffunction>
  
  <cffunction name="postUpdate" returntype="void" output="false">
    <cfset setPostUpdateTick(getTickCount()) />
  </cffunction>
  
</cfcomponent>

You'll notice that right before and right after each insert and update the current timer is pushed into the EventRecorder object. The same is done in our application event handler CFC:

component implements="cfide.orm.IEventHandler" {
  
  public void function preLoad(any entity) {}
  public void function postLoad(any entity) {}
  
  public void function preInsert (any entity) {
    entity.setSysPreInsertTick(getTickCount());
  }
  
  public void function postInsert (any entity) {
    entity.setSysPostInsertTick(getTickCount());
  }
  
  public void function preUpdate (any entity, struct oldData) {
    entity.setSysPreUpdateTick(getTickCount());
  }
  
  public void function postUpdate (any entity) {
    entity.setSysPostUpdateTick(getTickCount());
  }
  
  public void function preDelete(any entity) {}
  public void function postDelete(any entity) {}
  
}

The only requirement when creating an event handler CFC is that it must implement cfide.orm.IEventHandler.  While we only need 4 methods, remember that we need to implement each method with the appropriate signatures defined in the interface.  Don't forget that we need to tell ColdFusion that we want to enable event handler as well as define the event handler CFC:

this.ormSettings.eventHandling = true;
this.ormSettings.eventHandler = 'blogexamples.hql.cfc.EventHandler';

The cfm file that kicks it all off looks like this:

<cfscript>
  o = entityNew('EventRecorder');
  entitySave(o);
  ormFlush();
  
  entitySave(o);
  ormFlush();
</cfscript>

When the above was executed we see the following results:

  1. Does preUpdate fire if no properties have changed? Yes
  2. Does postUpdate fire if no properties have changed? Yes
  3. What is the order of operations when using both a CFC event handler and object event handler method? They appear to fire at the exact same time
I'm not happy with the results for #3.  Let's rerun the test with a delay set into both preUpdate methods by adding:

sleep(1000);


Eureka!  The class's event handler method fires before the application event handler CFC.

2 comments:

  1. Hi,

    have you seen a cfcomponent impelementing cfide.orm.IEventHandler in tag-notation. I keep getting "[mycomponent] is not a valid ORM event handler. "

    ReplyDelete
  2. It should work the same regardless of if you are using tags or script. Make sure you are implementing the methods correctly. There should be more information available in the log, or enable robust exception information in the CF Administrator.

    ReplyDelete