How to log changes of ActiveRecords?

You are viewing revision #11 of this wiki article.
This version may not be up to date with the latest version.
You may want to view the differences to the latest version or see the changes made in this revision.

« previous (#10)next (#12) »

A simple and effective way to keep track what your users are doing within your application is to log their activities related to database modifications. You can log whenever a record was inserted, changed or deleted, and also when and by which user this was done. For a [CActiveRecord] Model you could use a behavior for this purpose. This way you will be able to add log functionality to ActiveRecords very easily.

First of all you have to create a table for the log-lines in the database. Here is an example (MySQL):

[sql]
CREATE TABLE ActiveRecordLog (
  id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  description VARCHAR(255) NULL,
  action VARCHAR(20) NULL,
  model VARCHAR(45) NULL,
  idModel INTEGER UNSIGNED NULL,
  field VARCHAR(45) NULL,
  creationdate TIMESTAMP NOT NULL,
  userid VARCHAR(45) NULL,
  PRIMARY KEY(id)
)
TYPE=InnoDB;

The next step would be to create the corresponding Model using the YII Shell Tool:

model ActiveRecordLog

If you would like to have basic crud functionality created, you should also type:

crud ActiveRecordLog

To be able to log the changes we will use a behavior class. So you have to create one (i.e. ActiveRecordLogableBehavior) and store it somewhere in your application directory (i.e. \protected\behaviors). The behavior class should extend [CActiveRecordBehavior] as we want to work with ActiveRecords here.

class ActiveRecordLogableBehavior extends CActiveRecordBehavior
{
	private $_oldattributes = array();

	public function afterSave($event)
	{
		if (!$this->Owner->isNewRecord) {

			// new attributes
			$newattributes = $this->Owner->getAttributes();
			$oldattributes = $this->getOldAttributes();

			// compare old and new
            foreach ($newattributes as $name => $value) {
				if (!empty($oldattributes)) {
					$old = $oldattributes[$name];
				} else {
					$old = '';
				}

				if ($value != $old) {
					//$changes = $name . ' ('.$old.') => ('.$value.'), ';

					$log=new ActiveRecordLog;
					$log->description=	'User ' . Yii::app()->user->Name 
                                            . ' changed ' . $name . ' for ' 
                                            . get_class($this->Owner) 
                                            . '[' . $this->Owner->getPrimaryKey() .'].';
					$log->action=		'CHANGE';
					$log->model=		get_class($this->Owner);
					$log->idModel=		$this->Owner->getPrimaryKey();
					$log->field=		$name;
					$log->creationdate=	new CDbExpression('NOW()');
					$log->userid=		Yii::app()->user->id;
					$log->save();
				}
            }
		} else {
			$log=new ActiveRecordLog;
			$log->description=	'User ' . Yii::app()->user->Name 
                                    . ' created ' . get_class($this->Owner) 
                                    . '[' . $this->Owner->getPrimaryKey() .'].';
			$log->action=		'CREATE';
			$log->model=		get_class($this->Owner);
			$log->idModel=		$this->Owner->getPrimaryKey();
			$log->field=		'';
			$log->creationdate= new CDbExpression('NOW()');
			$log->userid=		Yii::app()->user->id;
			$log->save();
		}
	}

	public function afterDelete($event)
	{
		$log=new ActiveRecordLog;
		$log->description=	'User ' . Yii::app()->user->Name . ' deleted ' 
                                . get_class($this->Owner) 
                                . '[' . $this->Owner->getPrimaryKey() .'].';
		$log->action=		'DELETE';
		$log->model=		get_class($this->Owner);
		$log->idModel=		$this->Owner->getPrimaryKey();
		$log->field=		'';
		$log->creationdate= new CDbExpression('NOW()');
		$log->userid=		Yii::app()->user->id;
		$log->save();
	}

	public function afterFind($event)
	{
		// Save old values
		$this->setOldAttributes($this->Owner->getAttributes());
	}

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

	public function setOldAttributes($value)
	{
		$this->_oldattributes=$value;
	}
}

The behavior class uses the ActiveRecordLog Model to store the log lines into the database. It will log a line each time a record is inserted or deleted. It will also log a line for each field which is changed.

In order to make an ActiveRecord Model use this behavior, you have to add the following code to the Model class:

public function behaviors()
{
    return array(
        // Classname => path to Class
        'ActiveRecordLogableBehavior'=>
            'application.behaviors.ActiveRecordLogableBehavior',
    );
}

Of course this simple example could be enhanced:

  • support for mult-column primary keys
  • savethe attributeLabels instead of the field names
  • make description customizable
  • and so on...

Links

31 1
47 followers
Viewed: 74 207 times
Version: Unknown (update)
Category: Tutorials
Tags: Logging
Written by: pfth
Last updated by: Yang He
Created on: Feb 13, 2009
Last updated: 12 years ago
Update Article

Revisions

View all history

Related Articles