Skip to content

Instantly share code, notes, and snippets.

@index0h
Created March 29, 2015 23:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save index0h/fa7f41aa5f9d9a6e5224 to your computer and use it in GitHub Desktop.
Save index0h/fa7f41aa5f9d9a6e5224 to your computer and use it in GitHub Desktop.
History.php
<?php
/**
* Поведение для отслеживания изменений.
*
* **ВНИМАНИЕ!!!**
* Данное поведение не работает с моножественными действиями: update и delete, будьте внимательны.
*
* ### Подготовка:
*
* - Необходимо создать такую же таблицу с суфиксом **_history**, как отслежуемая, но с дополнительными полями:
*
* ```php
* <?php
* 'historyId' => 'BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY',
* 'historyState' => "ENUM('create','update','delete') NOT NULL COMMENT 'Тип изменения.'",
* 'historyDate' => "TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'Дата изменения.'",
* 'historyUserId' => "INT UNSIGNED NULL COMMENT 'Идентификатор пользователя, выполнившего изменение.'",
* 'historyContext' => "text NULL COMMENT 'JSON объект для дополнительных данных.'"
* ```
*
* - Так же для поля **id** **убираем** ключ **PRIMARY**.
* - Создаем модель для данный таблицы с суфиксом **History**.
* - Подключаем поведение в отслеживаемую модель.
*
* ```php
* <?php
* public function behaviors()
* {
* return array(
* 'history' => array(
* 'class' => '\some_namespace_here\History'
* )
* );
* }
* ```
*
* ### Использование:
*
* В общем тут все просто: при сохранении, изменении, или удалении отслеживаемой модели все действия с историей
* выполняются автоматически.
*
* Для сохранения смыслового контекста (причина изменений) - используется атрибут historyContext, в СУБД он
* сохраняется как JSON-строка.
*
* ```php
* <?php
* $model->history->context = array('userType' => 'guest');
*/
namespace some_namespace_here;
/**
* Поведение для отслеживания изменений.
*/
class History extends \CActiveRecordBehavior
{
/**
* Дата изменения, на случай необходимости задания "вручную".
*/
public $date;
/**
* Контекст изменения, должне сохранять допонтиельные данные связанные с причиной изменения.
*/
public $context;
/**
* Регистрирует изменение при вставке, или обновлении.
*
* @param \CEvent $event Событие вызова afterSave.
*/
public function afterSave($event)
{
$state = ($this->owner->scenario === 'insert') ? 'create': 'update';
$this->saveState($state);
}
/**
* Регистрирует удаление записи.
*
* @param \CEvent $event Событие вызова afterDelete.
*/
public function afterDelete($event)
{
$this->saveState('delete');
}
/**
* Выполняет подготовку атрибутов вспомогательной модели слежения перед ее сохранением.
*
* @param array $attributes Атрибуты обслуживаемой модели.
* @param string $state Состояние изменения (create, update, delete).
*/
protected function prepareAttributes($attributes, $state)
{
try {
$userId = \Yii::app()->user->id;
} catch (\Exception $error) {
// Возникает в случае консольного вызова, по этой причине не логгируется.
$userId = null;
}
$context = ($this->context === null)? null: json_encode($this->context);
$date = ($this->date === null)? components\Date::timestamp(): $this->date;
return \CMap::mergeArray(
$attributes,
array(
'historyState' => $state,
'historyDate' => $date,
'historyContext' => $context,
'historyUserId' => $userId
)
);
}
/**
* Создает и сохраняет вспомогательную модель слежения.
*
* @param $state Состояние изменения (create, update, delete).
*/
protected function saveState($state)
{
$historyModel = get_class($this->owner) . 'History';
$model = new $historyModel;
$attributes = $this->prepareAttributes($this->owner->attributes, $state);
foreach ($attributes as $key => $value) {
$model->$key = $value;
}
$model->insert();
$this->context = null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment