diff --git a/composer.json b/composer.json index 8e0ada3..791861a 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,8 @@ "require": { "php": ">=5.4.0", "illuminate/support": "~4.0|~5.0|~5.1|^6.0|^7.0|^8.0|^9.0|^10.0", - "laravel/framework": "~5.4|^6.0|^7.0|^8.0|^9.0|^10.0" + "laravel/framework": "~5.4|^6.0|^7.0|^8.0|^9.0|^10.0", + "fico7489/laravel-pivot": "3.0.*" }, "autoload": { "classmap": [ diff --git a/readme.md b/readme.md index 0e85c25..abd92ae 100644 --- a/readme.md +++ b/readme.md @@ -183,6 +183,15 @@ If you want to store the creation as a revision you can override this behavior b protected $revisionCreationsEnabled = true; ``` +### Traccking many to many relationship +By default the many to many relationship is not stored as a revision. +You can track BelongsToMany and MorphToMany attach and detach events. + +If you want to store these events as a revision you can override this behavior by setting `manyToManyRevisionEnabled` to `true` by adding the following to your model: +```php +protected $manyToManyRevisionEnabled = true; +``` + ## More Control No doubt, there'll be cases where you don't want to store a revision history only for certain fields of the model, this is supported in two different ways. In your model you can either specifiy which fields you explicitly want to track and all other fields are ignored: diff --git a/src/Venturecraft/Revisionable/RevisionableTrait.php b/src/Venturecraft/Revisionable/RevisionableTrait.php index 0def63f..1bd5c04 100644 --- a/src/Venturecraft/Revisionable/RevisionableTrait.php +++ b/src/Venturecraft/Revisionable/RevisionableTrait.php @@ -1,6 +1,9 @@ -postDelete(); $model->postForceDelete(); }); + + // ManyToMany listeners + static::pivotAttached(function ($model, $relationName, $pivotIds, $pivotIdsAttributes) { + $pivotClass = get_class($model->$relationName()->getRelated()); + $model->postPivotSimpleEvent('attached', $pivotClass, $pivotIds[0], $pivotIdsAttributes); + }); + + static::pivotDetached(function ($model, $relationName, $pivotIds, $pivotIdsAttributes) { + $pivotClass = get_class($model->$relationName()->getRelated()); + $model->postPivotSimpleEvent('detached', $pivotClass, $pivotIds[0], $pivotIdsAttributes); + }); } /** @@ -173,10 +189,11 @@ public function postSave() } else { $LimitReached = false; } + if (isset($this->revisionCleanup)){ - $RevisionCleanup=$this->revisionCleanup; - }else{ - $RevisionCleanup=false; + $RevisionCleanup = $this->revisionCleanup; + } else { + $RevisionCleanup = false; } // check if the model already exists @@ -203,12 +220,13 @@ public function postSave() } if (count($revisions) > 0) { - if($LimitReached && $RevisionCleanup){ + if ($LimitReached && $RevisionCleanup) { $toDelete = $this->revisionHistory()->orderBy('id','asc')->limit(count($revisions))->get(); - foreach($toDelete as $delete){ + foreach($toDelete as $delete) { $delete->delete(); } } + $revision = Revisionable::newModel(); \DB::table($revision->getTable())->insert($revisions); \Event::dispatch('revisionable.saved', array('model' => $this, 'revisions' => $revisions)); @@ -224,14 +242,12 @@ 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; } - if ((!isset($this->revisionEnabled) || $this->revisionEnabled)) - { + if ((!isset($this->revisionEnabled) || $this->revisionEnabled)) { $revisions[] = array( 'revisionable_type' => $this->getMorphClass(), 'revisionable_id' => $this->getKey(), @@ -314,6 +330,51 @@ public function postForceDelete() } } + /** + * Called after many to many record successfully attached or detached + * + * @param string $event + * @param string $pivotModel + * @param integer $pivotId + * @param array $pivotAttributes + * @return void + */ + public function postPivotSimpleEvent(string $event, string $pivotModel, int $pivotId, array $pivotAttributes): void + { + // Many to many revisions are disabled by default + if (!isset($this->manyToManyRevisionEnabled) || !$this->manyToManyRevisionEnabled) { + return; + } + + // Revision for pivots + $revisions[] = [ + 'revisionable_type' => $pivotModel, + 'revisionable_id' => $pivotId, + 'key' => "{$event}-to", + 'old_value' => get_class($this), + 'new_value' => $this->id, + 'user_id' => $this->getSystemUserId(), + 'created_at' => new \DateTime(), + 'updated_at' => new \DateTime(), + ]; + + // Revision for yourself + $revisions[] = [ + 'revisionable_type' => get_class($this), + 'revisionable_id' => $this->id, + 'key' => "{$event}-from", + 'old_value' => $pivotModel, + 'new_value' => $pivotId, + 'user_id' => $this->getSystemUserId(), + 'created_at' => new \DateTime(), + 'updated_at' => new \DateTime(), + ]; + + $revision = Revisionable::newModel(); + \DB::table($revision->getTable())->insert($revisions); + \Event::dispatch("revisionable.pivot.{$event}", ['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 @@ -338,7 +399,6 @@ public function getSystemUserId() return null; } - public function getAdditionalFields() { $additional = []; @@ -346,7 +406,7 @@ public function getAdditionalFields() //get them into an array. $fields = config('revisionable.additional_fields', []); foreach($fields as $field) { - if(Arr::has($this->originalData, $field)) { + if (Arr::has($this->originalData, $field)) { $additional[$field] = Arr::get($this->originalData, $field); } } @@ -398,6 +458,7 @@ private function isRevisionable($key) if (isset($this->doKeep) && in_array($key, $this->doKeep)) { return true; } + if (isset($this->dontKeep) && in_array($key, $this->dontKeep)) { return false; } @@ -496,6 +557,7 @@ public function disableRevisionField($field) if (!isset($this->dontKeepRevisionOf)) { $this->dontKeepRevisionOf = array(); } + if (is_array($field)) { foreach ($field as $one_field) { $this->disableRevisionField($one_field); @@ -522,10 +584,12 @@ public function disableRevisionField($field) */ private function sortJsonKeys($attribute) { - if(empty($attribute)) return $attribute; + if (empty($attribute)) { + return $attribute; + } foreach ($attribute as $key=>$value) { - if(is_array($value) || is_object($value)){ + if (is_array($value) || is_object($value)) { $value = $this->sortJsonKeys($value); } else { continue;