Propel 1.6 Gets Versionable Behavior - With A Twist
Propel 1.6 ships with a great new behavior. Once enabled on a table, the versionable behavior stores a copy of the ActiveRecord object in a separate table each time it is saved. This allows to keep track of the changes made on an object, whether to review modifications, or revert to a previous state.
The classic Wiki example is a good illustration:
<table name="wiki_page"> <column name="id" required="true" primaryKey="true" autoIncrement="true" type="INTEGER" /> <column name="title" type="VARCHAR" required="true" /> <column name="body" type="LONGVARCHAR" /> <behavior name="versionable" /> </table>
After rebuild, the WikiPage model has versioning abilities:
$page = new WikiPage();
// automatic version increment
$page->setTitle('Propel');
$page->setBody('Propel is a CRM built in PHP');
$page->save();
echo $page->getVersion(); // 1
$page->setBody('Propel is an ORM built in PHP5');
$page->save();
echo $page->getVersion(); // 2
// reverting to a previous version
$page->toVersion(1);
echo $page->getBody(); // 'Propel is a CRM built in PHP'
// saving a previous version creates a new one
$page->save();
echo $page->getVersion(); // 3
// checking differences between versions
print_r($page->compareVersions(1, 2));
// array(
// 'Body' => array(
// 1 => 'Propel is a CRM built in PHP',
// 2 => 'Propel is an ORM built in PHP5'
// ),
// );
// deleting an object also deletes all its versions
$page->delete();The versionable behavior offers audit log functionality, so you can track who made a modification, when, and why:
$page = new WikiPage();
$page->setTitle('PEAR');
$page->setBody('PEAR is a framework and distribution system for reusable PHP components');
$page->setVersionCreatedBy('John Doe');
$page->setVersionComment('First draft');
$page->save();
// do more modifications...
// list all modifications
foreach ($page->getAllVersions() as $pageVersion) {
echo sprintf("'%s', Version %d, updated by %s on %s (%s)\n",
$pageVersion->getTitle(),
$pageVersion->getVersion(),
$pageVersion->getVersionCreatedBy(),
$pageVersion->getVersionCreatedAt(),
$pageVersion->getVersionComment(),
);
}
// 'PEAR', Version 1, updated by John Doe on 2010-12-21 22:53:02 (First draft)
// 'PEAR', Version 2, updated by ...If it was just for that, the versionable behavior would already be awesome. Versioning is a very common feature, and there is no doubt that this behavior will replace lots of boilerplate code. Consider the fact that it’s very configurable, fully documented, and unit tested, and there is no reason to develop your own versioning layer.
But there is more.
The versionable behavior also works on relationships.
If the WikiPage has one Category, and if the Category model also uses the versionable behavior, then each time a WikiPage is saved, it saves the version of the related Category it is related to, and it is able to restore it:
$category = new Category();
$category->setName('Libraries');
$page = new WikiPage();
$page->setTitle('PEAR');
$page->setBody('PEAR is a framework and distribution system for reusable PHP components');
$page->setCategory($category);
$page->save(); // version 1
$page->setTitle('PEAR - PHP Extension and Application Repository');
$page->save(); // version 2
$category->setName('PHP Libraries');
$page->save(); // version 3
$page->toVersion(1);
echo $page->getTitle(); // 'PEAR'
echo $page->getCategory()->getName(); // 'Libraries'
$page->toVersion(3);
echo $page->getTitle(); // 'PEAR - PHP Extension and Application Repository'
echo $page->getCategory()->getName(); // 'PHP Libraries'Now the versioning is not limited to a single class anymore. You can even design a fully versionable "application" - it all depends on your imagination.
This feature is unique to Propel, and that’s our very Christmas gift to you.
16 comments
I mean, if I have several pages sharing one category, do you have to save each of those pages each time you make a change in their category?
I must say propel is getting really really nice since 1.5 and I like the direction your heading it to. keep up the good work :)
i've followed your instructions above, but when i'm rebuilding the model, i got the following errors
Some problems occurred when executing the task:
build-propel.xml:538:20: Table "sf_blog_post" contains a foreign key to nonexistent table "sf_guard_user"
build-propel.xml:525:22: Execution of the target buildfile failed. Aborting.
If the exception message is not clear enough, read the output of the task for
more information
Then it could be usefull to have a version label field. The we can do :
$object->saveNewVersion("John Doe version of the page with XWZ product information, have to be publish fo 3 feb 2012").
It could be usefull because sometime we prefer to have version management for a page which is not used for "automatic backup" but for "content workflow process".
Thanks for all this fantastic features !