|
<?php |
|
|
|
use Finite\StateMachine\StateMachine; |
|
|
|
/** |
|
* The FiniteAuditTrail Trait. |
|
* This plugin for the Finite package (see https://github.com/yohang/Finite) adds support for keeping an audit trail for any state machine. |
|
* Having an audit trail gives you a complete history of the state changes in your stateful model. |
|
* Prerequisites: |
|
* 1. Install Finite package (https://github.com/yohang/Finite#readme) |
|
* 2. Use FiniteStateMachine in your model (https://gist.github.com/tortuetorche/6365575) |
|
* Usage: in your Stateful Class use this trait after FiniteStateMachine trait, like this "use FiniteAuditTrail;". |
|
* Then call initAuditTrail() method at the end of initialization (__contruct() method) after initStateMachine() and parent::__construct() call. |
|
* Then create or complete the static boot() method in your model like this: |
|
* |
|
* public static function boot() |
|
* { |
|
* parent::boot(); |
|
* static::finiteAuditTrailBoot(); |
|
* } |
|
* |
|
* @author Tortue Torche <tortuetorche@spam.me> |
|
*/ |
|
trait FiniteAuditTrail |
|
{ |
|
public static function finiteAuditTrailBoot() |
|
{ |
|
static::saveInitialState(); |
|
} |
|
|
|
protected static function saveInitialState() |
|
{ |
|
static::created(function($model) { |
|
$transition = new Finite\Transition\Transition(null, null, $model->findInitialState()); |
|
$model->storeAuditTrail($model, $transition, false); |
|
}); |
|
} |
|
|
|
protected $auditTrailModel; |
|
protected $auditTrailAttributes;// We can't set an empty array as default value here, maybe a PHP Trait bug ? |
|
|
|
/** |
|
* @param mixed|array|args $args |
|
* if $args is an array: |
|
* initAuditTrail(['to' => 'ModelAuditTrail', 'attributes' => ['name', 'email']]); |
|
* else: initAuditTrail('ModelAuditTrail', ['name', 'email']); |
|
* first param: string $to Model name who stores the history |
|
* second param: string|array $attributes Attribute(s) or method(s) from stateful model to save |
|
*/ |
|
protected function initAuditTrail($args = null) |
|
{ |
|
// Default options |
|
$options = [ 'attributes' => (array) $this->auditTrailAttributes, 'to' => "\\".get_called_class()."StateTransition" ]; |
|
|
|
if (func_num_args() === 2) { |
|
$args = func_get_args(); |
|
list($options['to'], $options['attributes']) = $args; |
|
} elseif ( func_num_args() === 1) { |
|
if (is_array($args)) { |
|
$newOptions = array_extract_options($args); |
|
if(empty($newOptions)) { |
|
$options['attributes'] = $args; |
|
} else { |
|
$options = array_merge($options, $newOptions); |
|
} |
|
} elseif ( is_string($args) ) { |
|
$options['to'] = $args; |
|
} |
|
} |
|
|
|
$this->auditTrailAttributes = (array) $options['attributes']; |
|
$this->auditTrailModel = $options['to']; |
|
|
|
$this->addAfter([$this, 'storeAuditTrail']); |
|
|
|
} |
|
|
|
|
|
/** |
|
* Create a new model instance that is existing. |
|
* |
|
* @param array $attributes |
|
* @return \Illuminate\Database\Eloquent\Model|static |
|
*/ |
|
public function newFromBuilder($attributes = []) |
|
{ |
|
$instance = parent::newFromBuilder($attributes); |
|
$this->restoreAuditTrail($instance); |
|
return $instance; |
|
} |
|
|
|
/** |
|
* @param \Illuminate\Database\Eloquent\Model|static $instance |
|
*/ |
|
protected function restoreAuditTrail($instance) |
|
{ |
|
$instance->getStateMachine()->initialize(); |
|
} |
|
|
|
/** |
|
* @param object $self |
|
* @param Finite\Event\TransitionEvent|Finite\Transition\Transition $transitionEvent |
|
* @param boolean $save Optional, default: true |
|
*/ |
|
public function storeAuditTrail($self, $transitionEvent, $save = true) |
|
{ |
|
if ($save === true || $this->exists === false) { |
|
$this->save(); |
|
} |
|
if ($transitionEvent instanceof Finite\Event\TransitionEvent) { |
|
$transition = $transitionEvent->getTransition(); |
|
} else { |
|
$transition = $transitionEvent; |
|
} |
|
|
|
$values = []; |
|
$values['event'] = $transition->getName(); |
|
$initialStates = $transition->getInitialStates(); |
|
if (! empty($initialStates)) { |
|
$values['from'] = $transitionEvent->getInitialState()->getName(); |
|
} |
|
|
|
$values['to'] = $transition->getState(); |
|
$values[snake_case(get_called_class()).'_id'] = $this->getKey(); |
|
|
|
|
|
$auditTrail = new $this->auditTrailModel; |
|
foreach ((array) $this->auditTrailAttributes as $attribute) { |
|
if ($this->getAttribute($attribute)) { |
|
$values[$attribute] = $this->getAttribute($attribute); |
|
} |
|
} |
|
|
|
$auditTrail->fill($values); |
|
$auditTrail->save(); |
|
} |
|
} |