From 4e51cd3a7c5efb04928000533d6492154da1f521 Mon Sep 17 00:00:00 2001 From: alustau Date: Wed, 26 Apr 2017 18:01:19 -0300 Subject: [PATCH 1/4] Refactoring to remove somes duplicated code --- src/Venturecraft/Revisionable/CommonTrait.php | 305 ++++++++++++++++++ .../Revisionable/Revisionable.php | 277 +--------------- .../Revisionable/RevisionableTrait.php | 301 +---------------- 3 files changed, 309 insertions(+), 574 deletions(-) create mode 100644 src/Venturecraft/Revisionable/CommonTrait.php diff --git a/src/Venturecraft/Revisionable/CommonTrait.php b/src/Venturecraft/Revisionable/CommonTrait.php new file mode 100644 index 00000000..8c8a0427 --- /dev/null +++ b/src/Venturecraft/Revisionable/CommonTrait.php @@ -0,0 +1,305 @@ +preSave(); + }); + + static::saved(function ($model) { + $model->postSave(); + }); + + static::created(function($model){ + $model->postCreate(); + }); + + static::deleted(function ($model) { + $model->preSave(); + $model->postDelete(); + }); + } + + /** + * @return mixed + */ + public function revisionHistory() + { + return $this->morphMany(Revision::class, 'revisionable'); + } + + /** + * Invoked before a model is saved. Return false to abort the operation. + * + * @return bool + */ + public function preSave() + { + if (!isset($this->revisionEnabled) || $this->revisionEnabled) { + // if there's no revisionEnabled. Or if there is, if it's true + + $this->originalData = $this->original; + $this->updatedData = $this->attributes; + + // we can only safely compare basic items, + // so for now we drop any object based items, like DateTime + foreach ($this->updatedData as $key => $val) { + if (gettype($val) == 'object' && !method_exists($val, '__toString')) { + unset($this->originalData[$key]); + unset($this->updatedData[$key]); + array_push($this->dontKeep, $key); + } + } + + // the below is ugly, for sure, but it's required so we can save the standard model + // then use the keep / dontkeep values for later, in the isRevisionable method + $this->dontKeep = isset($this->dontKeepRevisionOf) ? + array_merge($this->dontKeepRevisionOf, $this->dontKeep) + : $this->dontKeep; + + $this->doKeep = isset($this->keepRevisionOf) ? + array_merge($this->keepRevisionOf, $this->doKeep) + : $this->doKeep; + + unset($this->attributes['dontKeepRevisionOf']); + unset($this->attributes['keepRevisionOf']); + + $this->dirtyData = $this->getDirty(); + $this->updating = $this->exists; + } + } + + /** + * Attempt to find the user id of the currently logged in user + * Supports Cartalyst Sentry/Sentinel based authentication, as well as stock Auth + **/ + public function getSystemUserId() + { + try { + if (class_exists($class = '\SleepingOwl\AdminAuth\Facades\AdminAuth') + || class_exists($class = '\Cartalyst\Sentry\Facades\Laravel\Sentry') + || class_exists($class = '\Cartalyst\Sentinel\Laravel\Facades\Sentinel') + ) { + return ($class::check()) ? $class::getUser()->id : null; + } elseif (\Auth::check()) { + return \Auth::user()->getAuthIdentifier(); + } + } catch (\Exception $e) { + return null; + } + + return null; + } + + /** + * Get all of the changes that have been made, that are also supposed + * to have their changes recorded + * + * @return array fields with new data, that should be recorded + */ + private function changedRevisionableFields() + { + $changes_to_record = array(); + foreach ($this->dirtyData as $key => $value) { + // check that the field is revisionable, and double check + // that it's actually new data in case dirty is, well, clean + if ($this->isRevisionable($key) && !is_array($value)) { + if (!isset($this->originalData[$key]) || $this->originalData[$key] != $this->updatedData[$key]) { + $changes_to_record[$key] = $value; + } + } else { + // we don't need these any more, and they could + // contain a lot of data, so lets trash them. + unset($this->updatedData[$key]); + unset($this->originalData[$key]); + } + } + + return $changes_to_record; + } + + /** + * Check if this field should have a revision kept + * + * @param string $key + * + * @return bool + */ + private function isRevisionable($key) + { + + // If the field is explicitly revisionable, then return true. + // If it's explicitly not revisionable, return false. + // Otherwise, if neither condition is met, only return true if + // we aren't specifying revisionable fields. + if (isset($this->doKeep) && in_array($key, $this->doKeep)) { + return true; + } + if (isset($this->dontKeep) && in_array($key, $this->dontKeep)) { + return false; + } + + return empty($this->doKeep); + } + + /** + * Check if soft deletes are currently enabled on this model + * + * @return bool + */ + private function isSoftDelete() + { + // check flag variable used in laravel 4.2+ + if (isset($this->forceDeleting)) { + return !$this->forceDeleting; + } + + // otherwise, look for flag used in older versions + if (isset($this->softDelete)) { + return $this->softDelete; + } + + return false; + } + + /** + * @return mixed + */ + public function getRevisionFormattedFields() + { + return $this->revisionFormattedFields; + } + + /** + * @return mixed + */ + public function getRevisionFormattedFieldNames() + { + return $this->revisionFormattedFieldNames; + } + + /** + * Identifiable Name + * When displaying revision history, when a foreign key is updated + * instead of displaying the ID, you can choose to display a string + * of your choice, just override this method in your model + * By default, it will fall back to the models ID. + * + * @return string an identifying name for the model + */ + public function identifiableName() + { + return $this->getKey(); + } + + /** + * Revision Unknown String + * When displaying revision history, when a foreign key is updated + * instead of displaying the ID, you can choose to display a string + * of your choice, just override this method in your model + * By default, it will fall back to the models ID. + * + * @return string an identifying name for the model + */ + public function getRevisionNullString() + { + return isset($this->revisionNullString) ? $this->revisionNullString : 'nothing'; + } + + /** + * No revision string + * When displaying revision history, if the revisions value + * cant be figured out, this is used instead. + * It can be overridden. + * + * @return string an identifying name for the model + */ + public function getRevisionUnknownString() + { + return isset($this->revisionUnknownString) ? $this->revisionUnknownString : 'unknown'; + } + + /** + * Disable a revisionable field temporarily + * Need to do the adding to array longhanded, as there's a + * PHP bug https://bugs.php.net/bug.php?id=42030 + * + * @param mixed $field + * + * @return void + */ + public function disableRevisionField($field) + { + if (!isset($this->dontKeepRevisionOf)) { + $this->dontKeepRevisionOf = array(); + } + if (is_array($field)) { + foreach ($field as $one_field) { + $this->disableRevisionField($one_field); + } + } else { + $donts = $this->dontKeepRevisionOf; + $donts[] = $field; + $this->dontKeepRevisionOf = $donts; + unset($donts); + } + } +} diff --git a/src/Venturecraft/Revisionable/Revisionable.php b/src/Venturecraft/Revisionable/Revisionable.php index 7099ef50..ab661145 100644 --- a/src/Venturecraft/Revisionable/Revisionable.php +++ b/src/Venturecraft/Revisionable/Revisionable.php @@ -15,113 +15,13 @@ */ class Revisionable extends Eloquent { - /** - * @var - */ - private $originalData; - - /** - * @var - */ - private $updatedData; - - /** - * @var - */ - private $updating; - - /** - * @var array - */ - private $dontKeep = array(); - - /** - * @var array - */ - private $doKeep = array(); - - /** - * Keeps the list of values that have been updated - * - * @var array - */ - protected $dirtyData = array(); - - /** - * Create the event listeners for the saving and saved events - * This lets us save revisions whenever a save is made, no matter the - * http method. - */ - public static function boot() - { - parent::boot(); - - static::saving(function ($model) { - $model->preSave(); - }); - - static::saved(function ($model) { - $model->postSave(); - }); - - static::created(function($model){ - $model->postCreate(); - }); - - static::deleted(function ($model) { - $model->preSave(); - $model->postDelete(); - }); - } - - /** - * @return mixed - */ - public function revisionHistory() - { - return $this->morphMany('\Venturecraft\Revisionable\Revision', 'revisionable'); - } + use CommonTrait; /** * Invoked before a model is saved. Return false to abort the operation. * * @return bool */ - public function preSave() - { - if (!isset($this->revisionEnabled) || $this->revisionEnabled) { - // if there's no revisionEnabled. Or if there is, if it's true - - $this->originalData = $this->original; - $this->updatedData = $this->attributes; - - // we can only safely compare basic items, - // so for now we drop any object based items, like DateTime - foreach ($this->updatedData as $key => $val) { - if (gettype($val) == 'object' && ! method_exists($val, '__toString')) { - unset($this->originalData[$key]); - unset($this->updatedData[$key]); - } - } - - // the below is ugly, for sure, but it's required so we can save the standard model - // then use the keep / dontkeep values for later, in the isRevisionable method - $this->dontKeep = isset($this->dontKeepRevisionOf) ? - array_merge($this->dontKeepRevisionOf, $this->dontKeep) - : $this->dontKeep; - - $this->doKeep = isset($this->keepRevisionOf) ? - array_merge($this->keepRevisionOf, $this->doKeep) - : $this->doKeep; - - unset($this->attributes['dontKeepRevisionOf']); - unset($this->attributes['keepRevisionOf']); - - $this->dirtyData = $this->getDirty(); - $this->updating = $this->exists; - } - } - /** * Called after a model is successfully saved. @@ -159,6 +59,7 @@ public function postSave() } } + /** * Called after record successfully created */ @@ -188,7 +89,6 @@ public function postCreate() $revision = new Revision; \DB::table($revision->getTable())->insert($revisions); - } } @@ -214,177 +114,4 @@ public function postDelete() \DB::table($revision->getTable())->insert($revisions); } } - - /** - * Attempt to find the user id of the currently logged in user - * Supports Cartalyst Sentry/Sentinel based authentication, as well as stock Auth - **/ - private function getSystemUserId() - { - try { - if (class_exists($class = '\Cartalyst\Sentry\Facades\Laravel\Sentry') - || class_exists($class = '\Cartalyst\Sentinel\Laravel\Facades\Sentinel')) { - return ($class::check()) ? $class::getUser()->id : null; - } elseif (\Auth::check()) { - return \Auth::user()->getAuthIdentifier(); - } - } catch (\Exception $e) { - return null; - } - - return null; - } - - /** - * Get all of the changes that have been made, that are also supposed - * to have their changes recorded - * - * @return array fields with new data, that should be recorded - */ - private function changedRevisionableFields() - { - $changes_to_record = array(); - foreach ($this->dirtyData as $key => $value) { - // check that the field is revisionable, and double check - // that it's actually new data in case dirty is, well, clean - if ($this->isRevisionable($key) && !is_array($value)) { - if (!isset($this->originalData[$key]) || $this->originalData[$key] != $this->updatedData[$key]) { - $changes_to_record[$key] = $value; - } - } else { - // we don't need these any more, and they could - // contain a lot of data, so lets trash them. - unset($this->updatedData[$key]); - unset($this->originalData[$key]); - } - } - - return $changes_to_record; - } - - /** - * Check if this field should have a revision kept - * - * @param string $key - * - * @return bool - */ - private function isRevisionable($key) - { - - // If the field is explicitly revisionable, then return true. - // If it's explicitly not revisionable, return false. - // Otherwise, if neither condition is met, only return true if - // we aren't specifying revisionable fields. - if (isset($this->doKeep) && in_array($key, $this->doKeep)) { - return true; - } - if (isset($this->dontKeep) && in_array($key, $this->dontKeep)) { - return false; - } - return empty($this->doKeep); - } - - /** - * Check if soft deletes are currently enabled on this model - * - * @return bool - */ - private function isSoftDelete() - { - // check flag variable used in laravel 4.2+ - if (isset($this->forceDeleting)) { - return !$this->forceDeleting; - } - - // otherwise, look for flag used in older versions - if (isset($this->softDelete)) { - return $this->softDelete; - } - - return false; - } - - /** - * @return mixed - */ - public function getRevisionFormattedFields() - { - return $this->revisionFormattedFields; - } - - /** - * @return mixed - */ - public function getRevisionFormattedFieldNames() - { - return $this->revisionFormattedFieldNames; - } - - /** - * Identifiable Name - * When displaying revision history, when a foreign key is updated - * instead of displaying the ID, you can choose to display a string - * of your choice, just override this method in your model - * By default, it will fall back to the models ID. - * - * @return string an identifying name for the model - */ - public function identifiableName() - { - return $this->getKey(); - } - - /** - * Revision Unknown String - * When displaying revision history, when a foreign key is updated - * instead of displaying the ID, you can choose to display a string - * of your choice, just override this method in your model - * By default, it will fall back to the models ID. - * - * @return string an identifying name for the model - */ - public function getRevisionNullString() - { - return isset($this->revisionNullString)?$this->revisionNullString:'nothing'; - } - - /** - * No revision string - * When displaying revision history, if the revisions value - * cant be figured out, this is used instead. - * It can be overridden. - * - * @return string an identifying name for the model - */ - public function getRevisionUnknownString() - { - return isset($this->revisionUnknownString)?$this->revisionUnknownString:'unknown'; - } - - /** - * Disable a revisionable field temporarily - * Need to do the adding to array longhanded, as there's a - * PHP bug https://bugs.php.net/bug.php?id=42030 - * - * @param mixed $field - * - * @return void - */ - public function disableRevisionField($field) - { - if (!isset($this->dontKeepRevisionOf)) { - $this->dontKeepRevisionOf = array(); - } - if (is_array($field)) { - foreach ($field as $one_field) { - $this->disableRevisionField($one_field); - } - } else { - $donts = $this->dontKeepRevisionOf; - $donts[] = $field; - $this->dontKeepRevisionOf = $donts; - unset($donts); - } - } } diff --git a/src/Venturecraft/Revisionable/RevisionableTrait.php b/src/Venturecraft/Revisionable/RevisionableTrait.php index f298dcf7..08e5a611 100644 --- a/src/Venturecraft/Revisionable/RevisionableTrait.php +++ b/src/Venturecraft/Revisionable/RevisionableTrait.php @@ -13,85 +13,7 @@ */ trait RevisionableTrait { - /** - * @var array - */ - private $originalData = array(); - - /** - * @var array - */ - private $updatedData = array(); - - /** - * @var boolean - */ - private $updating = false; - - /** - * @var array - */ - private $dontKeep = array(); - - /** - * @var array - */ - private $doKeep = array(); - - /** - * Keeps the list of values that have been updated - * - * @var array - */ - protected $dirtyData = array(); - - /** - * Ensure that the bootRevisionableTrait is called only - * if the current installation is a laravel 4 installation - * Laravel 5 will call bootRevisionableTrait() automatically - */ - public static function boot() - { - parent::boot(); - - if (!method_exists(get_called_class(), 'bootTraits')) { - static::bootRevisionableTrait(); - } - } - - /** - * Create the event listeners for the saving and saved events - * This lets us save revisions whenever a save is made, no matter the - * http method. - * - */ - public static function bootRevisionableTrait() - { - static::saving(function ($model) { - $model->preSave(); - }); - - static::saved(function ($model) { - $model->postSave(); - }); - - static::created(function($model){ - $model->postCreate(); - }); - - static::deleted(function ($model) { - $model->preSave(); - $model->postDelete(); - }); - } - - /** - * @return mixed - */ - public function revisionHistory() - { - return $this->morphMany('\Venturecraft\Revisionable\Revision', 'revisionable'); - } + use CommonTrait; /** * Generates a list of the last $limit revisions made to any objects of the class it is being called from. @@ -106,47 +28,6 @@ public static function classRevisionHistory($limit = 100, $order = 'desc') ->orderBy('updated_at', $order)->limit($limit)->get(); } - /** - * Invoked before a model is saved. Return false to abort the operation. - * - * @return bool - */ - public function preSave() - { - if (!isset($this->revisionEnabled) || $this->revisionEnabled) { - // if there's no revisionEnabled. Or if there is, if it's true - - $this->originalData = $this->original; - $this->updatedData = $this->attributes; - - // we can only safely compare basic items, - // so for now we drop any object based items, like DateTime - foreach ($this->updatedData as $key => $val) { - if (gettype($val) == 'object' && !method_exists($val, '__toString')) { - unset($this->originalData[$key]); - unset($this->updatedData[$key]); - array_push($this->dontKeep, $key); - } - } - - // the below is ugly, for sure, but it's required so we can save the standard model - // then use the keep / dontkeep values for later, in the isRevisionable method - $this->dontKeep = isset($this->dontKeepRevisionOf) ? - array_merge($this->dontKeepRevisionOf, $this->dontKeep) - : $this->dontKeep; - - $this->doKeep = isset($this->keepRevisionOf) ? - array_merge($this->keepRevisionOf, $this->doKeep) - : $this->doKeep; - - unset($this->attributes['dontKeepRevisionOf']); - unset($this->attributes['keepRevisionOf']); - - $this->dirtyData = $this->getDirty(); - $this->updating = $this->exists; - } - } - /** * Called after a model is successfully saved. @@ -209,8 +90,7 @@ public function postCreate() // Check if we should store creations in our revision history // Set this value to true in your model if you want to - if(empty($this->revisionCreationsEnabled)) - { + if(empty($this->revisionCreationsEnabled)) { // We should not store creations. return false; } @@ -232,7 +112,6 @@ public function postCreate() \DB::table($revision->getTable())->insert($revisions); \Event::fire('revisionable.created', array('model' => $this, 'revisions' => $revisions)); } - } /** @@ -259,180 +138,4 @@ public function postDelete() \Event::fire('revisionable.deleted', array('model' => $this, 'revisions' => $revisions)); } } - - /** - * Attempt to find the user id of the currently logged in user - * Supports Cartalyst Sentry/Sentinel based authentication, as well as stock Auth - **/ - public function getSystemUserId() - { - try { - if (class_exists($class = '\SleepingOwl\AdminAuth\Facades\AdminAuth') - || class_exists($class = '\Cartalyst\Sentry\Facades\Laravel\Sentry') - || class_exists($class = '\Cartalyst\Sentinel\Laravel\Facades\Sentinel') - ) { - return ($class::check()) ? $class::getUser()->id : null; - } elseif (\Auth::check()) { - return \Auth::user()->getAuthIdentifier(); - } - } catch (\Exception $e) { - return null; - } - - return null; - } - - /** - * Get all of the changes that have been made, that are also supposed - * to have their changes recorded - * - * @return array fields with new data, that should be recorded - */ - private function changedRevisionableFields() - { - $changes_to_record = array(); - foreach ($this->dirtyData as $key => $value) { - // check that the field is revisionable, and double check - // that it's actually new data in case dirty is, well, clean - if ($this->isRevisionable($key) && !is_array($value)) { - if (!isset($this->originalData[$key]) || $this->originalData[$key] != $this->updatedData[$key]) { - $changes_to_record[$key] = $value; - } - } else { - // we don't need these any more, and they could - // contain a lot of data, so lets trash them. - unset($this->updatedData[$key]); - unset($this->originalData[$key]); - } - } - - return $changes_to_record; - } - - /** - * Check if this field should have a revision kept - * - * @param string $key - * - * @return bool - */ - private function isRevisionable($key) - { - - // If the field is explicitly revisionable, then return true. - // If it's explicitly not revisionable, return false. - // Otherwise, if neither condition is met, only return true if - // we aren't specifying revisionable fields. - if (isset($this->doKeep) && in_array($key, $this->doKeep)) { - return true; - } - if (isset($this->dontKeep) && in_array($key, $this->dontKeep)) { - return false; - } - - return empty($this->doKeep); - } - - /** - * Check if soft deletes are currently enabled on this model - * - * @return bool - */ - private function isSoftDelete() - { - // check flag variable used in laravel 4.2+ - if (isset($this->forceDeleting)) { - return !$this->forceDeleting; - } - - // otherwise, look for flag used in older versions - if (isset($this->softDelete)) { - return $this->softDelete; - } - - return false; - } - - /** - * @return mixed - */ - public function getRevisionFormattedFields() - { - return $this->revisionFormattedFields; - } - - /** - * @return mixed - */ - public function getRevisionFormattedFieldNames() - { - return $this->revisionFormattedFieldNames; - } - - /** - * Identifiable Name - * When displaying revision history, when a foreign key is updated - * instead of displaying the ID, you can choose to display a string - * of your choice, just override this method in your model - * By default, it will fall back to the models ID. - * - * @return string an identifying name for the model - */ - public function identifiableName() - { - return $this->getKey(); - } - - /** - * Revision Unknown String - * When displaying revision history, when a foreign key is updated - * instead of displaying the ID, you can choose to display a string - * of your choice, just override this method in your model - * By default, it will fall back to the models ID. - * - * @return string an identifying name for the model - */ - public function getRevisionNullString() - { - return isset($this->revisionNullString) ? $this->revisionNullString : 'nothing'; - } - - /** - * No revision string - * When displaying revision history, if the revisions value - * cant be figured out, this is used instead. - * It can be overridden. - * - * @return string an identifying name for the model - */ - public function getRevisionUnknownString() - { - return isset($this->revisionUnknownString) ? $this->revisionUnknownString : 'unknown'; - } - - /** - * Disable a revisionable field temporarily - * Need to do the adding to array longhanded, as there's a - * PHP bug https://bugs.php.net/bug.php?id=42030 - * - * @param mixed $field - * - * @return void - */ - public function disableRevisionField($field) - { - if (!isset($this->dontKeepRevisionOf)) { - $this->dontKeepRevisionOf = array(); - } - if (is_array($field)) { - foreach ($field as $one_field) { - $this->disableRevisionField($one_field); - } - } else { - $donts = $this->dontKeepRevisionOf; - $donts[] = $field; - $this->dontKeepRevisionOf = $donts; - unset($donts); - } - } } From e013ba67f152308950fb56026dbcfc980de55170 Mon Sep 17 00:00:00 2001 From: alustau Date: Wed, 26 Apr 2017 18:16:17 -0300 Subject: [PATCH 2/4] Adding session_user_id to update user_id when you using API --- src/Venturecraft/Revisionable/CommonTrait.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Venturecraft/Revisionable/CommonTrait.php b/src/Venturecraft/Revisionable/CommonTrait.php index 8c8a0427..ad7cbd32 100644 --- a/src/Venturecraft/Revisionable/CommonTrait.php +++ b/src/Venturecraft/Revisionable/CommonTrait.php @@ -133,6 +133,8 @@ public function preSave() **/ public function getSystemUserId() { + $sessionUserId = app('request')->input('session_user_id', null); + try { if (class_exists($class = '\SleepingOwl\AdminAuth\Facades\AdminAuth') || class_exists($class = '\Cartalyst\Sentry\Facades\Laravel\Sentry') @@ -141,6 +143,8 @@ public function getSystemUserId() return ($class::check()) ? $class::getUser()->id : null; } elseif (\Auth::check()) { return \Auth::user()->getAuthIdentifier(); + } elseif ($sessionUserId) { + return $sessionUserId; } } catch (\Exception $e) { return null; From 4f827f0e40f3e1a472397e45da349835ec255390 Mon Sep 17 00:00:00 2001 From: Denis Alustau Date: Thu, 27 Apr 2017 15:53:28 -0300 Subject: [PATCH 3/4] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 52677d68..187bfbf9 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "venturecraft/revisionable", + "name": "alustau/revisionable", "license": "MIT", "description": "Keep a revision history for your models without thinking, created as a package for use with Laravel", "keywords": ["model", "laravel", "ardent", "revision", "history"], From 152f8d9519c4c1c02ecd8e4bbe7465d2cca9822e Mon Sep 17 00:00:00 2001 From: Denis Alustau Date: Thu, 27 Apr 2017 16:27:21 -0300 Subject: [PATCH 4/4] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 187bfbf9..52677d68 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "alustau/revisionable", + "name": "venturecraft/revisionable", "license": "MIT", "description": "Keep a revision history for your models without thinking, created as a package for use with Laravel", "keywords": ["model", "laravel", "ardent", "revision", "history"],