cfWheels: hasChanged() and changedFrom()

One of the more overlooked nice features of cfWheels is the ability to determine whether an object’s properties have changed before you save them. With that information, we can then easily set other model properties dependent on the change.

Why would we do this? Let’s take an example of a ticketing system where each ticket would have a status, i.e “Available, Pending Payment, Sold, Void”, but for each of those conditions, other columns in your dataset might need to be altered, and you only want to alter them when the status actually changes, not every time the object is saved.

So if a ticket’s Status goes from from Pending Payment-> Sold, we might want to change another column as part of the work flow, in this case, we might want to dictate that now that ticket is Sold, that it now needs to be printed out and delivered. In our example, we want to programmatically change deliverystatus to “Print Me” or something. Maybe we’ve got a condition setup which removes tickets from a shopping basket via cron job when a date has expired (these are just examples).

In wheels, we can add this logic once on the model, and then we never need to worry about it in our controllers again. The trick is using this.changedFrom(“ticketstatus”) and this.ticketstatus as the “before and after” values to compare against.

In our Ticket.cfc model’s init() method, we can add a function call to trigger beforeSave:

beforeSave("checkTicketConditions");

Then, in the same file, add a function to handle this:

public void function checkTicketConditions() {

  if(this.hasChanged("ticketstatus")){
    oldStatus=this.changedFrom("ticketstatus");
    newStatus=this.ticketstatus;

    // Reserving Tickets in shopping basket
    if(newStatus EQ "INBASKET"){
      this.removeFromBasketAt=dateAdd("s", 3200, now());
    }

    // Remove cron basket timelimit (i.e, when the status changes from INBASKET to anything)
    if(oldStatus EQ "INBASKET"){
      this.removeFromBasketAt="";
    }

    // etc
  }

  // Update delivery status
  if(this.hasChanged("delivery")){
    if(this.delivery EQ "eticket"){
      this.deliverystatus="PENDING EMAIL DELIVERY";
    } else if(this.delivery EQ "postal"){
      this.deliverystatus="PRINT AND POST ME";
    } else if(this.delivery EQ "pickup"){
      this.deliverystatus="PRINT ME";
    }
  }
}

With this type of model logic you can actually fire off some pretty complex workflows, and keep it all out of your controller!