Question

I'm creating an audit trail module that i will put in a larger system; and i've created a table to store the trail entries , as an "auditor" what i want to see the currently logged on user, the page where he/she is in, what action he/she did, and what were the changes and when...

these are basically what i want to see; my audit trail table looks like:

User| Timestamp| Module Name| Action| Old Value| New Value| Description

i basically had no problem getting the user, by

Yii::app()->session['username'];

the page/module and action by getting the controller's :

$this->module->getName();
$this->action->id;

My problem lies with the changes old value to new value, the edits done by the user. i could sort of "sniff" out what edits/ changes he/she did by literally copying the variables and passing it through my function where i create the log.. How do i do this dynamically?

i sort of want to detect if a certain model's properties or attributes has been changed and see what changes were made so that i could get a detail log...Thanks ! sorry, i'm really trying hard to explain this.

Was it helpful?

Solution

In each model that you want to observe you can write a afterFind() method, where you store the current DB attributes into some private variable, e.b. _dbValues. Then in beforeSave() you verify the current attributes with the ones in _dbValues and create an audit record if there was a change.

After you have this working, you can take it a step further and create a behavior from it. You'd put the private variable, the afterFind() and the beforeSave() method there. Then you can attach that behavior to many records.

OTHER TIPS

Quick example:

class Book extends CActiveRecord
{
    private $oldAttrs = array();

    public static function model($className = __CLASS__)
    {
        return parent::model($className);
    }

    public function tableName()
    {
        return 'book';
    }

    protected function afterSave()
    {
        // store history
        if (!$this->isNewRecord) {
            $newAttrs = $this->getAttributes();
            $oldAttrs = $this->getOldAttributes();

            // your code
        }

        return parent::afterSave();
    }

    protected function afterFind()
    {
        // Save old values
        $this->setOldAttributes($this->getAttributes());

        return parent::afterFind();
    }

    public function getOldAttributes()
    {
        return $this->oldAttrs;
    }

    public function setOldAttributes($attrs)
    {
        $this->oldAttrs = $attrs;
    }
}

Your solution is good, but what if there're 2 threads that call ->save() at the same time?

Assume that:

  1. the 1st thread find record, save the A status.
  2. the 2nd thread find record, save the A status.
  3. then 1st thread change record to B, call ->save(). System will log A->B
  4. then 2nd thread change record to C, call ->save(). System will log A->C

summary, there're 2 log: A->B, A->C. If this is not problem for you, just ignore it and do the above solution.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top