Yii 1.1: audittrail2

A reload of the popular audittrail extension
32 followers

Please use the Github project home for this extension: HERE since the download here is out of date. Github is the officially supported platform for me to dish out updates.

This is basically a modification of a previous extension made by MadSkillsTisdale at http://www.yiiframework.com/extension/audittrail.

I have basically cleaned up some of the code and made a few additions to the behaviour bundled within this extension.

Installing the extension

The method of installation has changed. I have removed the need to install a module since:

  • It only provided global configuration variables for the audit log widget
  • It was extra bloat that didn't justify the needs
  • I found that in a real system you wouldn't want a page showing all audit log entries since the audit logs
  • The audit log is quite easy to add to a page using CGridView

As such, for these reasons, the module itself has been deleted.

Composer

This extension is listed on packagist.

Step 1

To install you must first choose a folder in which to place this repository. I have chosen:

/root/backend/extensions/modules

Since this seems most right to me. Clone this repository to that location.

Step 2

Time to install the table. You can use the migration file provided by the original author of this extension or you can use the SQL file bundled within the migrations folder. Simply run it on your DB server (using PHPMyAdmin or something) and watch the magic unfold.

Step 3

Reference the AuditTrail model within your configuration:

'import'=>array(
    'site.backend.extensions.modules.auditTrail.models.AuditTrail',
),

Note You can move AuditTrail to your models folder preventing you from having to link it like this.

Step 4

Simply use the behaviour within a model like:

'LoggableBehavior'=> array(
    'class' => 'site.backend.extensions.modules.auditTrail.behaviors.LoggableBehavior',
)

Epilogue

If your user class is not User then you may (depending on your setup) need to change the relation within the AuditLog model to suite your needs.

API

Please note that the below snippets are snippets only and are not tested in a real environment. It is recommend that you use this section as reference and documentation only, do not copy and paste from here.

Custom User Attributes

Some people don't actually have defined users but do have an attribute of the auditable model that would define a unique identification of who edited it. For this end you can use:

'LoggableBehavior'=> array(
    'class' => 'site.backend.extensions.modules.auditTrail.behaviors.LoggableBehavior',
    'userAttribute' => 'name'
)

Storing Timestamps

The date of the audit log can be changed to used timestamps instead using:

'LoggableBehavior'=> array(
    'class' => 'site.backend.extensions.modules.auditTrail.behaviors.LoggableBehavior',
    'storeTimestamp' => true
)

Changing the date format

You can adjust the date format using the dateFormat property of the behaviour:

'LoggableBehavior'=> array(
    'class' => 'site.backend.extensions.modules.auditTrail.behaviors.LoggableBehavior',
    'dateFormat' => 'Y-m-d H:i:s'
)

Ignoring and allowing specific fields

There is one interesting addition to this version. You can now specify an allowed set of fields and a ignored set of fields...or both.

To do this include the behaviour in your models like you normally would:

'LoggableBehavior'=> 'site.backend.extensions.modules.auditTrail.behaviors.LoggableBehavior'

But then add either an ignored or allowed (or both) list of fields to the behaviour like so:

'LoggableBehavior'=> array(
    'class' => 'site.backend.extensions.modules.auditTrail.behaviors.LoggableBehavior',
    'allowed' => array(
        'version',
        'ns_purchase_description'
    ),
    'ignored' => array(
        'ns_purchase_description',
        'ns_display_name',
        'update_time'
    )
)

The names put into the allowed and ignored parameters of the behaviour represent field names.

As you will notice I allow the ns_purchase_description field but also ignore it. When you use the fields in this way ignored will replace the allowed and this field will be omitted.

Ignoring a whole class

This is useful if you put the behaviour in a class that extends CActiveRecord which all your own models extend from. This is useful in cases where you want ALL your classes to share the same core (like this behaviour) without having to specify it in every model you create.

'LoggableBehavior'=> array(
    'class' => 'site.backend.extensions.modules.auditTrail.behaviors.LoggableBehavior',
    'ignored_class' => array(
        'ErrorLog',  // I use this to log error messages to MYSQL, no need to keep a log of this
    ),
)

Printing out the audit log

Since this no longer uses a module to do its work there is no global configuration for the previously inbuilt audit log to work from. Instead you can insert an audit log like (as an example only, showing an audit of changes to a book title and it's products on a book title page):

$model_ids = array(array($model->id, 'Title'));
foreach($model->products as $id => $product){
    $model_ids[] = array($product->id, 'Product');
}

$criteria=new CDbCriteria(array(
    'order'=>'stamp DESC',
    'with'=>array('user'),
));
$param_id = 0;
foreach( $model_ids as $id_pair ) {
    $criteria->addCondition( '( model_id = :id' . $param_id . ' AND model = :model' . $param_id . ' )', 'OR' );
    $criteria->params[ ':id' . $param_id ] = $id_pair[0];
    $criteria->params[ ':model' . $param_id ] = $id_pair[1];
    $param_id++;
}

$this->widget('zii.widgets.grid.CGridView', array(
    'id'=>'title-grid',
    'dataProvider'=>new CActiveDataProvider('AuditTrail', array(
        'criteria'=>$criteria,
        'pagination'=>array(
            'pageSize'=>100,
        )
    )),
    'columns'=>array(
        array(
            'name' => 'Author',
            'value' => '$data->user ? $data->user->email : ""'
        ),
        'model',
        'model_id',
        'action',
        array(
            'name' => 'field',
            'value' => '$data->getParent()->getAttributeLabel($data->field)'
        ),
        'old_value',
        'new_value',
        array(
            'name' => 'Date Changed',
            'value' => 'date("d-m-Y H:i:s", strtotime($data->stamp))'
        )
    ),
));

Total 20 comments

#17394 report it
jcagentzero at 2014/06/03 05:51am
Saving with mysql transaction

Hi,

I am using this extension and it is pretty good, but we encountered an issue due to our transactional data saving.

Scenario: In a single save, I need to save the values to 2 different fields. It was successful on the first table but encountered an error on the second table. Now the problem is, the audit trail recorded the first action. But since mysql has rollback function, the record in audit trail will be different from the actual record.

#14880 report it
Sammaye at 2013/09/18 04:04am
Re: Error

Fixed thanks

#14875 report it
buminda at 2013/09/17 11:31am
Error

create index idx_audit_trail_old_value on tbl_audit_trail (old_value) ...exception 'CDbException' with message 'CDbCommand failed to execute the SQL statement: SQLSTATE[42000]: Syntax error or access violation: 1170 BLOB/TEXT column 'old_value' used in key specification without a key length. The SQL statement executed was: CREATE INDEX idx_audit_trail_old_value ON tbl_audit_trail (old_value)' in /usr/share/php/yii-1.1.12.b600af/framework/db/CDbCommand.php:354 Stack trace:

#14873 report it
Sammaye at 2013/09/17 07:04am
Re: 2 indices

Weird that should work, definitely a bug if it does not. What was the exact error you got?

#14872 report it
buminda at 2013/09/17 06:54am
2 indices

1.$this->createIndex( 'idx_audit_trail_old_value', 'tbl_audit_trail', 'old_value');

2.$this->createIndex( 'idx_audit_trail_new_value', 'tbl_audit_trail', 'new_value');

#14870 report it
Sammaye at 2013/09/17 04:36am
Re: Inserting behaviour and creating indexes issue

Thanks for the comment.

About point 2: what indexes exactly? There are about 7 normally.

#14868 report it
buminda at 2013/09/17 04:25am
Inserting behaviour and creating indexes issue
  1. To be more precise loggable behaviour needs to be inside as below ,

    public function behaviors() {
    return array( // Classname => path to Class 'LoggableBehavior'=> array( 'class' => 'ext.audittrail.behaviors.LoggableBehavior',
    ) ); }

  2. Migrate will not work unless you comment the creation of indexes for 2 test columns ( with mysql )

#13985 report it
Zeam at 2013/07/10 04:27pm
Added the ability to ignore a whole class, including added comments to the readme

I submitted a pull request to github, hopefully you update the article above :)

#13749 report it
Sammaye at 2013/06/21 01:43pm
RE: Old Value

How do you declare your primaryKey? I should be bringing it back as a json string

#11960 report it
Sammaye at 2013/02/15 07:14am
RE: Old Value

I have opened a new toic here: http://www.yiiframework.com/forum/index.php/topic/40416-extension-audittrail-2/

You can also post an issue on github: https://github.com/Sammaye/audittrail/issues?state=open

#11959 report it
Paul_Kish at 2013/02/15 06:41am
RE RE: Old Value

Can you open a forum topic for this extension, or if one exist post the link?

#11958 report it
Sammaye at 2013/02/15 06:12am
RE: Old Value

Yeah, that is very weird.

I would try and see what getAttributes returns and if it returns fields that are not empty I would iterate through auditAttributes to see why it ditches them.

I can't see anything obvious from the classes.

#11957 report it
Paul_Kish at 2013/02/15 06:00am
Old Value

@Sammaye It seems to get the old value on some models but on others nothing is stored, let me investigate, here are dropbox link to the two different models,does not work on this one Example One Works on this one Example Two . I have also tried playing with

public $skipNulls = true;

nothing much, I am using the github version

#11954 report it
Sammaye at 2013/02/15 05:08am
RE: Old Value

Strange indeed.

Can you post a code snippet that can replicate this behaviour?

Maybe it is how it is being used in the model as a behaviour in the parent model.

Also can you just tell me if getAttributes() actually returns anything in this case?

It might not be recording old values because those values are either '' or null, these values I omit to save database space; that option can be turned off however.

#11953 report it
Paul_Kish at 2013/02/15 04:59am
Old Value

I cant seem to get Old value to be recorded, its seems this is tied to

$this->Owner->getAttributes()

Any pointers, thanks again, the allowed and ignored functionality is really awesome

#9764 report it
Sammaye at 2012/09/08 06:26pm
RE: create delete trail

I will look into that getIsNewRecord one though it does work on my system.

Ah I see, it is running the validation rules. Strange I don't see that behaviour in my system but you are right that is a bug. I have fixed it now :).

#9763 report it
onemoverx at 2012/09/08 06:15pm
create delete trail

you have this in your rules for model AuditTrail array('action, model, field, stamp, model_id', 'required'),

but when you create a trail for create (there another issue here I think) or delete only pass variable $action and save fails. another thing I changed is that you use afterSave to save the trail and you use isNewRecord, I'm not sure right now cause I can't check it right away but that will always be false, because the model is already saved so you have to check that flag in beforeSave and ask latter in the afterSave.

Hope it helps sorry if I'm wrong :D

#9762 report it
Sammaye at 2012/09/08 05:34pm
RE: some bugs

Fixed time bug.

Would be awesome if you can give an example of the Delete thing you said about :)

#9761 report it
Sammaye at 2012/09/08 05:29pm
RE: some bugs

Strange I use delete and what not and it works fine for me. It should place "Create" (or "Delete") and the field attribute, as for the model_id: that is indeed a required attribute. Is there a reason you are using this without activeRecord since all active record models should have an id (primary key).

I shall look into that time thing. I must admit I had not tested that functionality, however $time() is not a valid variable. Can you give me an example code which causes the bugable behaviour?

Edit: I see what you meant now by $time(), lol. I will change that asap.

#9760 report it
onemoverx at 2012/09/08 04:36pm
some bugs

There are some issues with audittrails2, for example is unable to record create and delete trails because model_id and field attributes in model AuditTrail are required so save fails every time. Another bug is found in LoggableBehavior in line 101, when using variable $time() instead of function time(), by default it would not give error because the default is to not save timestamp.

thanks for your extension, very usefull to me!

Leave a comment

Please to leave your comment.

Create extension
Downloads
No downloadable files yet