Revision Behavior - Revision control made easy
Take full control of any changes your users makes, while also giving them features like undo. Keep a history of previous versions of any database model, allowing you to undo, revert to an older version (or a specific time), manage and inspect changes and even get a difference array for seeing changes over time to any (or all) fields.
RevisionBehavior is a solution for adding undo and other versioning functionality
to your database models. It is set up for easy application to your project,
ease of use and to not get in the way of your other model activity.
It is also intended to work well with it's sibling, LogableBehavior.
You should make these AFTER you have baked your ordinary tables as they may interfer. By default
the tables should be named rev_[normal table name]. If you wish to change the prefix you may
do so in the property called $revision_prefix found in the behavior. Also by default the behavior expects
the shadow tables to be in the same dbconfig as the model, but you may change this on a per
model basis with the useDbConfig config option.
row in the shadow table will (in most cases) be the same as the current live data.
The exception is when the ignore field functionality is used and the live data is
updated only in those fields.
you can add those fields to the configuration ignore array. Any time the behavior's
afterSave is called with just primary key and these fields, it will NOT generate
a new revision. It WILL however save these fields together with other fields when it
does save a revision. You will probably want to set up cron or otherwise call
createRevision() to update these fields at some points.
beforeSave and afterSave. In afterSave, the behavior will save a new revision of the dataset
that is now the live data. If you do NOT want this automatic behavior, you may set the config
option 'auto' to false. Then the shadow table will remain empty unless you call createRevisions
manually.
to your database models. It is set up for easy application to your project,
ease of use and to not get in the way of your other model activity.
It is also intended to work well with it's sibling, LogableBehavior.
Feature list
- Easy to install
- Automagically save revision on model save
- Able to ignore model saves which only contain certain fields
- Limit number of revisions to keep, will delete oldest
- Undo functionality (or update to any revision directly)
- Revert to a datetime (and even do so cascading)
- Get a diff model array to compare two or more revisions
- Inspect any or all revisions of a model
Install instructions
- Place the newest version of RevisionBehavior in your app/models/behaviors folder
- Add the behavior to AppModel (or single models if you prefer)
- For each model that you want revision for, create a shadow table
- Behavior will gracefully do nothing for models that has behavior, but not shadow table
- If adding Revision to an existing project, run the initializeRevisions() method once for each model.
About shadow tables
You should make these AFTER you have baked your ordinary tables as they may interfer. By default
the tables should be named rev_[normal table name]. If you wish to change the prefix you may
do so in the property called $revision_prefix found in the behavior. Also by default the behavior expects
the shadow tables to be in the same dbconfig as the model, but you may change this on a per
model basis with the useDbConfig config option.
Add the same fields as in the live table, with 3 important differences.
- The 'id' field should NOT be the primary key, nor auto increment.
- Add the fields 'version_id' (int, primary key, autoincrement) and 'version_created' (datetime).
- Skipp fields that should not be saved in shadowtable (lft,right,weight for instance).
Configuration
When adding 'Revision' the a model's actsAs array, you may configure the behavior with these options:
- limit : int number of revisions to keep, must be at least 2 (as current is 1).
- ignore : array containing the name of fields to ignore.
- auto : boolean when false the behavior will NOT generate revisions in afterSave.
- useDbConfig : string/null Name of dbConfig to use. Null to use Model's.
Limit functionality
The shadow table will save a revision copy when it saves live data, so the newestrow in the shadow table will (in most cases) be the same as the current live data.
The exception is when the ignore field functionality is used and the live data is
updated only in those fields.
Ignore field(s) functionality
If you wish to be able to update certain fields without generating new revisions,you can add those fields to the configuration ignore array. Any time the behavior's
afterSave is called with just primary key and these fields, it will NOT generate
a new revision. It WILL however save these fields together with other fields when it
does save a revision. You will probably want to set up cron or otherwise call
createRevision() to update these fields at some points.
Auto functionality
By default the behavior will insert itself into the Model's save process by implementingbeforeSave and afterSave. In afterSave, the behavior will save a new revision of the dataset
that is now the live data. If you do NOT want this automatic behavior, you may set the config
option 'auto' to false. Then the shadow table will remain empty unless you call createRevisions
manually.
Comments
Comment
1 Looks great!
Question
2 Related models
Does it automagically take snapshots of the related models as well, i.e the true current state of the model?
E.g., I have a Category model [id, name] that hasMany Subcategories [id, category_id, name]. Can the RevisionBehavior answer questions like "Which Subcategories did a Category have at a specific time?"
Comment
3 Re: Related models
Comment
4 New version
Direct link to update code : http://code.google.com/p/alkemann/source/browse/trunk/models/behaviors/revision.php
Important notice : Due to a new requirement of having the ShadowModel inherit the table prefix from the Model, the internal logic of building a shadowtable name changed. In this process a new naming convention was establish, that I believe is an improvement. So the new rule is :
[any prefix][model_table_name]_revs
Example: users => users_revs, project_posts => project_posts_revs, model_prefix_comments => model_prefix_comments_revs
(Make sure you are using version 1.1 or later before renaming your tables to this)
Comment
5 Great job
Comment
6 Version 2
Comment
7 aftersave problem
$data = $Model->find('first', array('contain'=> $habtm, 'conditions'=>array($Model->alias.'.'.$Model->primaryKey => $Model->id)));
returns the record before the save, not the record after the save. removing "'contain'=> $habtm" returns the correct record, but obviously includes all related models.
do you have any idea why this happens? using cake_1.2.1.8004 and revision behavior 2.0.3
Comment
8 diff()
eg. field1 has init value of x1 and field2 has an init value of y1.
at time1, you change field1 to x2
at time2, you change field2 to y2
diff() returns
[0] => time2 {field1: x2, field2: y2} <--- x2 is here!?
[1] => time1 {field1: (blank), field2: (blank)}
[2] => init {field1: x1, field2: y1}
looking at this, there is no way to determine when field1 changed from x1 to x2 (since the value is blank for time1).
what it should return is
[0] => time2 {field1: (blank), field2: y2}
[1] => time1 {field1: x2, field2: (blank)}
[2] => init {field1: x1, field2: y1}
Comment
9 Re: aftersave problem
Your suggested changes to diff() are now implemented in svn