Google Ad

CakePHP Icing Plugin -- Versioning

Date: Thu, Oct 18th 2012, 15:14 Author: nick Views: 24748 Comments share

CakePHP Icing Plugin

Get it:
  • Download Now
  • git clone git:// Plugin/Icing
Hi all, I've been working on a catch-all CakePHP Plugin for my company with a number of coligues and I think we've developed enough to blog about some of the cooler features it offers. I'm going to go through them one at a time and post in depth on each one.
The first one I want to talk about is Versioning.

Model Versioning -- The Ability to UNDO

Have you ever wanted to go back in time with content of a record? Do you have multiple users who can make changes on your data and you'd like a way to track who did what, when and a way to revert to a previous version? How about being able to snapshot your record and include other model associations in your version of that snapshot? VersionableBehavior to the rescue!

Attach to any model to creating versions of current state on save for later restoration.
Uses the AuthComponent to log the user doing the save by default.

The plugin requires a single table icing_versions to be installed in your application. This is the core table that will handle any model's current version data and allow for you to not only go back and time and see what was changed, but who changed it, when, and what other models were affected by the change. You can also restore from previous version by either knowing the version id, or by simply saying "go back 2 revisions" and the plugin handles the rest.

Install Versionable

Run the schema into your database to create icing_versions table.

  1. cake schema create -p Icing

You should see icing_versions in your database

Usage Examples

Bind to model you want to auto-version on save
Default Settings:

  1.     'contain' => array(), //only version the current model
  2.     'versions' => false, //unlimited versions
  3.     'minor_timeframe' => false, //do not mark for minor versions
  4.     'bind' => false, //don't bind versions on find
  5.   )

  1. public $actsAs = array('Icing.Versionable'); //default settings
  1. public $actsAs = array('Icing.Versionable' => array(
  2.     'contain'         => array('Hour'), //contains for relative model to be included in the version.
  3.     'versions'        => '5',           //how many version to save at any given time (false by default unlimited)
  4.     'minor_timeframe' => '10',          //Mark all previous versions if saved within 10 seconds of current version.  Easily cleanup minor_versions
  5.     'bind'            => true,          //attach IcingVersion as HasMany relationship for you on find and if contained
  6.   ));

Settings Explained

contain : Containable array of other associated models to also version along with each version. If your model's snapshot also depends on other tables and models, simply add them to the contain option so it's included in the versioning (and restorable)

versions : Number of versions to allow on any paticular record (default unlimited). This is essentially the "undo" limit on your model your versioning. If it's a rather active record and you have unlimited versions the versions table will eventually get very large and slow, consider using an versions limit that will automatically clean up after itself.

minor_timeframe : Seconds between saves to mark previous as a minor version. Minor versions are then cleaned off periodically on demand by the user. The idea is if you make 10 saves within a minute, 9 of those saves are minor versions and really shouldn't be kept as major changes, whereas the last save includes all the changes and is the major version. Default false (minor versions are not kept track of)

bind : Default False. If true on non-contained retrevals of data on the versioned model, all related versions for each record will also be returned to the user. This is a dynamic bind. You can turn this feature off completely and just use it by containing on the model.

  1. $this->find('first', array(
  2.   'conditions' => array(
  3.     '' => $id,
  4.   ),
  5.   'contain' => array(
  6.     'IcingVersion' => array('order' => 'IcingVersion.created DESC')
  7.   )
  8. ));

The dynamic bind will happen for you automatically, you don't have to define your custom bind on the Model.

Restoring from a version

Restore version id.
  1. $this->Model->restoreVersion('50537471-ba08-44ae-a606-24e5e017215a'); //restores version id 50537471-ba08-44ae-a606-24e5e017215a

Restore from a version id and don't create a new version before restoring by passing boolean false in restoreVersion
  1. $this->Model->restoreVersion('50537471-ba08-44ae-a606-24e5e017215a', false); //restores version id 50537471-ba08-44ae-a606-24e5e017215a and won't create a new version before restoring.

Restore by just iterating back in time on a record. No need to remember the version ID.

Restore second version back from most recent on Model with the record id of 3
  1. $this->Model->restoreVersion(2, 3); //restores the second version back from most recent on Model id 3

Restore the second version back from the most recent Mode with record id of 3 and don't create a new version before restoring.
  1. $this->Model->restoreVersion(2, 3, false); //restores the second version back from most recent on Model id 3 and doesn't create a new version before saving

Seeing the difference between versions

You can easily see the difference between two different versions (or the current version HEAD) of the current record at any time.

Get the difference between a specific version and the current state of the record the version is of.
  1. $result = $this->Model->diffVersion('50537471-ba08-44ae-a606-24e5e017215a'); //Gets the diff between version id and the curent state of the record.

Get the difference between two specific versions.
  1. $result = $this->Model->diffVersion('50537471-ba08-44ae-a606-24e5e017215a', '501234121-ba08-44ae-a606-2asdf767a'); //Gets the diff between two different versions.

Save without creating a version

You can make a save on your versionable model without creating a new version by passing 'create_version' => false as an option to the Model save or saveAll options.

  1. $this->Model->save($data, array('create_version' => false));


As always, enjoy and comment!