Skip to content

Commit

Permalink
Merge pull request #15 from atk4/feature/add-insert-delete-log
Browse files Browse the repository at this point in the history
Feature/add insert delete log
  • Loading branch information
romaninsh authored Oct 3, 2016
2 parents e52cef4 + f0c50b7 commit 76ee945
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 68 deletions.
74 changes: 53 additions & 21 deletions src/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ class Controller {
public $custom_action = null;
public $custom_fields = [];

function __construct($m = null, $options = [])
function __construct($a = null, $options = [])
{
$this->audit_model = $m ?: $m = new model\AuditLog();
$this->audit_model = $a ?: $a = new model\AuditLog();

foreach($options as $key => $value) {
foreach ($options as $key => $value) {
$this->$key = $value;
}
}
Expand All @@ -33,7 +33,7 @@ function __construct($m = null, $options = [])
*/
function setUp(\atk4\data\Model $m)
{
$m->addHook('beforeUpdate,afterUpdate', $this);
$m->addHook('beforeSave,afterSave,beforeDelete,afterDelete', $this);
$m->addRef('AuditLog', function($m) {
$a = clone $this->audit_model;
$m->persistence->add($a);
Expand Down Expand Up @@ -70,12 +70,10 @@ function push(\atk4\data\Model $m, $action)
$this->custom_action = null;
}

$a['request_diff'] = $this->getDiffs($m);
$a['ts'] = new \DateTime();
$a['model'] = get_class($m);
$a['model_id'] = $m->id;
$a['action'] = $action;
$a['descr'] = $action.' '.$this->getDescr($a['request_diff']);

if ($this->custom_fields) {
$a->set($this->custom_fields);
Expand All @@ -96,26 +94,17 @@ function push(\atk4\data\Model $m, $action)
$a->start_mt = microtime();

array_unshift($this->audit_log_stack, $a);
return $a;
}

function pull(\atk4\data\Model $m)
{
$a = array_shift($this->audit_log_stack);
$a['reactive_diff'] = $this->getDiffs($m);
if($a['reactive_diff'] === $a['request_diff']) {
// Don't store reactive diff if it's identical to requested diff
unset($a['reactive_diff']);
} else {
$x = $a['reactive_diff'];

$a['descr'].= ' (resulted in '.$this->getDescr($a['reactive_diff']).')';
}

if ($this->record_time_taken) {
$a['time_taken'] = microtime() - $a->start_mt;
}

$a->save();
return $a;
}

function getDiffs(\atk4\data\Model $m)
Expand All @@ -127,15 +116,57 @@ function getDiffs(\atk4\data\Model $m)
return $diff;
}

function beforeUpdate(\atk4\data\Model $m)
function beforeSave(\atk4\data\Model $m)
{
if(!$m->loaded()) {
$a = $this->push($m, $action = 'create');
} else {
$a = $this->push($m, $action = 'update');
}
$a['request_diff'] = $this->getDiffs($m);
$a['descr'] = $action.' '.$this->getDescr($a['request_diff']);
}

function afterSave(\atk4\data\Model $m)
{
$this->push($m, 'update');
$a = $this->pull($m);

if ($a['model_id'] === null) {
// new record
$a['reactive_diff'] = $m->get();
$a['model_id'] = $m->id;
} else {
$a['reactive_diff'] = $this->getDiffs($m);
if ($a['reactive_diff'] === $a['request_diff']) {
// Don't store reactive diff if it's identical to requested diff
unset($a['reactive_diff']);
} else {
$x = $a['reactive_diff'];

$a['descr'].= ' (resulted in '.$this->getDescr($a['reactive_diff']).')';
}
}

$a->save();
}

function beforeDelete(\atk4\data\Model $m)
{
$a = $this->push($m, 'delete');
if ($m->only_fields) {
$id = $m->id;
$m = $m->newInstance()->load($id); // we need all fields
}
$a['request_diff'] = $m->get();
$a['descr'] = 'delete id='.$m->id;
if ($m->title_field && $m->hasElement($m->title_field)) {
$a['descr'] .= ' ('.$m[$m->title_field].')';
}
}

function afterUpdate(\atk4\data\Model $m)
function afterDelete(\atk4\data\Model $m)
{
$this->pull($m);
$this->pull($m)->save();
}

function getDescr($diff)
Expand All @@ -146,4 +177,5 @@ function getDescr($diff)
}
return join(', ', $t);
}

}
46 changes: 34 additions & 12 deletions src/model/AuditLog.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class AuditLog extends \atk4\data\Model {

public $controller = null;

public $order_field = 'id';

function init()
{
parent::init();
Expand All @@ -34,6 +36,8 @@ function init()

$this->addField('is_reverted', ['type' => 'boolean']);
$this->hasOne('revert_audit_log_id', new AuditLog());

$this->setOrder($this->order_field.' desc');
}

function loadLast()
Expand Down Expand Up @@ -62,26 +66,44 @@ function undo()

$this->atomic(function() {
$m = new $this['model']($this->persistence);
$m->load($this['model_id']);

foreach ($this['request_diff'] as $field => list($old, $new)) {
if ($m[$field] !== $new) {
throw new \atk4\core\Exception([
'New value does not match current. Risky to undo',
'new' => $new, 'current' => $m[$field]
]);
}

$m[$field] = $old;
}
$f = 'undo_'.$this['action'];

$m->audit_log_controller->custom_action = 'undo '.$this['action'];
$m->audit_log_controller->custom_fields['revert_audit_log_id'] = $this->id;

$m->save();
$this->$f($m);

$this['is_reverted'] = true;
$this->save();
});
}

function undo_update($m)
{
$m->load($this['model_id']);

foreach ($this['request_diff'] as $field => list($old, $new)) {
if ($m[$field] !== $new) {
throw new \atk4\core\Exception([
'New value does not match current. Risky to undo',
'new' => $new, 'current' => $m[$field]
]);
}

$m[$field] = $old;
}

$m->save();
}
function undo_delete($m)
{
$m->set($this['request_diff']);
$m->save();
}
function undo_create($m)
{
$m->load($this['model_id']);
$m->delete();
}
}
90 changes: 55 additions & 35 deletions tests/CRUDTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,8 @@ function init()
*/
class CRUDTest extends \atk4\schema\PHPUnit_SchemaTestCase
{
public function testUpdate()
{

$q = [
'user' => [
['name' => 'John', 'surname' => 'Smith'],
['name' => 'Steve', 'surname' => 'Jobs'],
],
'audit_log' => [
'_' => [
private $audit_db = ['_' => [
'initiator_audit_log_id' => 1,
'ts' => '',
'model' => '',
Expand All @@ -45,27 +37,37 @@ public function testUpdate()
'descr' => '',
'is_reverted' => '',
'revert_audit_log_id' => 1
]
]];


public function testUpdate()
{

$q = [
'user' => [
['name' => 'Vinny', 'surname' => 'Shira'],
['name' => 'Zoe', 'surname' => 'Shatwell'],
],
'audit_log' => $this->audit_db,
];
$this->setDB($q);

$m = new AuditableUser($this->db);

$m->tryLoadAny();
$m['name'] = 'QQ';
$m['name'] = 'Ken';
$m->save();

$l = $m->ref('AuditLog');
$l->loadLast();

$this->assertEquals('update name=QQ', $l['descr']);
$this->assertEquals(['name' => ['John', 'QQ']], $l['request_diff']);
$this->assertEquals('update name=Ken', $l['descr']);
$this->assertEquals(['name' => ['Vinny', 'Ken']], $l['request_diff']);

$m->load(2);
$m['name'] = 'XX';
$m['name'] = 'Brett';
$m->save();
$m['name'] = 'YY';
$m['name'] = 'Doug';
$m->save();
$this->assertEquals(2, $m->ref('AuditLog')->action('count')->getOne());
}
Expand All @@ -75,45 +77,63 @@ public function testUndo()

$q = [
'user' => [
['name' => 'John', 'surname' => 'Smith'],
['name' => 'Steve', 'surname' => 'Jobs'],
],
'audit_log' => [
'_' => [
'initiator_audit_log_id' => 1,
'ts' => '',
'model' => '',
'model_id' => 1,
'action' => '',
'user_info' => '',
'time_taken' => 1.1,
'request_diff' => '',
'reactive_diff' => '',
'descr' => '',
'is_reverted' => '',
'revert_audit_log_id' => 1
]
['name' => 'Jawshua', 'surname' => 'Lo'],
['name' => 'Jessica', 'surname' => 'Fish'],
],
'audit_log' => $this->audit_db,
];
$this->setDB($q);
$zz = $this->getDB('user');

$m = new AuditableUser($this->db);

$m->tryLoadAny();
$m['name'] = 'QQ';
$m['name'] = 'Donald';
$m->save();

$l = $m->ref('AuditLog');
$l->loadLast()->undo();


$m->reload();
$this->assertEquals('John', $m['name']);
$this->assertEquals('Jawshua', $m['name']);
$this->assertEquals(2, $m->ref('AuditLog')->action('count')->getOne());

$l = $m->ref('AuditLog')->loadLast();

$this->assertEquals(1, $l['revert_audit_log_id']);
$this->assertEquals(false, $l['is_reverted']);

// table is back to how it was
$this->assertEquals($zz, $this->getDB('user'));
}

public function testAddDelete()
{

$q = [
'user' => [
['name' => 'Jason', 'surname' => 'Dyck'],
['name' => 'James', 'surname' => 'Knight'],
],
'audit_log' => $this->audit_db,
];
$this->setDB($q);
$zz = $this->getDB('user');

$m = new AuditableUser($this->db);

$m->getElement('surname')->default = 'Pen';

$m->save(['name'=>'Robert']);

$m->loadBy('name', 'Jason')->delete();

$log = $m->ref('AuditLog');

$log->each('undo');

// table is back to how it was
$this->assertEquals($zz, $this->getDB('user'));
}
}

0 comments on commit 76ee945

Please sign in to comment.