Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Versionable Laravel Package

mpociot/versionable

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require mpociot/versionable
    php artisan vendor:publish --provider="Mpociot\Versionable\Providers\ServiceProvider" --tag="migrations"
    php artisan migrate
    
    • Publish migrations to customize the versions table (e.g., add user_id for soft-deletes or reasons for versioning triggers).
  2. Enable Versioning: Use the Versionable trait in your model:

    use Mpociot\Versionable\Traits\Versionable;
    
    class Post extends Model
    {
        use Versionable;
    }
    
    • No additional configuration needed for basic versioning.
  3. First Use Case:

    • Trigger Versioning: Save a model to create its first version:
      $post = new Post(['title' => 'Hello World']);
      $post->save(); // Creates version 1
      
    • Check Versions:
      $versions = $post->versions()->count(); // Returns 1
      

Implementation Patterns

Core Workflows

  1. Versioning Triggers:

    • Automatic: Versioning occurs on save(), update(), or delete() (configurable via versionableEvents).
      protected $versionableEvents = [
          'creating', 'updating', 'deleting', 'restoring'
      ];
      
    • Manual: Trigger versioning explicitly:
      $post->createVersion(); // Force-create a version
      
  2. Restoring Models:

    • Revert to a previous version:
      $post->versions()->find(1)->revert(); // Restores version 1
      
    • Restore via model instance:
      $oldPost = $post->versions()->find(1)->getModel();
      $post->restore($oldPost); // Merges changes from version 1
      
  3. Querying Versions:

    • Fetch versions with relationships:
      $versions = $post->versions()->with('user')->get();
      
    • Filter versions by date or attributes:
      $post->versions()->where('created_at', '>', now()->subDays(7))->get();
      
  4. Soft-Deletes Integration:

    • Enable soft-deletes in versions table:
      use Illuminate\Database\Eloquent\SoftDeletes;
      
      class Post extends Model
      {
          use Versionable, SoftDeletes;
      }
      
    • Restore soft-deleted versions:
      $post->versions()->onlyTrashed()->restore();
      
  5. Custom Version Attributes:

    • Track additional metadata (e.g., changed_by):
      protected $versionableAttributes = [
          'title', 'content', 'changed_by'
      ];
      

Integration Tips

  • Events: Listen to versionable.created or versionable.restored:
    event(new VersionableCreated($post, $version));
    
  • Observers: Extend versioning logic:
    class PostObserver extends ModelObserver {
        public function saving($post) {
            if ($post->isDirty('content')) {
                $post->setVersionableAttribute('content_reviewed', false);
            }
        }
    }
    
  • APIs: Expose version history via API:
    Route::get('/posts/{post}/versions', function (Post $post) {
        return $post->versions()->with('user')->get();
    });
    

Gotchas and Tips

Pitfalls

  1. Performance:

    • Large Models: Versioning all attributes can bloat storage. Use $versionableAttributes to limit fields.
    • Indexing: Add indexes to versions table for model_id, created_at:
      Schema::table('versions', function (Blueprint $table) {
          $table->index(['model_id', 'created_at']);
      });
      
    • Soft-Deletes: Soft-deleting versions may slow queries. Use withTrashed() sparingly.
  2. Data Conflicts:

    • Concurrent Edits: Restoring a version may overwrite unsaved changes. Use transactions:
      DB::transaction(function () use ($post, $oldPost) {
          $post->restore($oldPost);
      });
      
    • Circular References: Avoid restoring versions with recursive relationships (e.g., PostCommentPost).
  3. Configuration Quirks:

    • Event Overrides: If versionableEvents is empty, no versions are created. Defaults to ['creating', 'updating'].
    • Timestamps: Ensure created_at/updated_at are not excluded from versioning if relying on them for auditing.

Debugging

  1. Missing Versions:

    • Check versionableEvents and model observers for early save() calls.
    • Verify versionableAttributes includes critical fields (e.g., title but not updated_at).
  2. Restoration Issues:

    • Use getModel() to inspect the version’s state before restoring:
      $oldData = $post->versions()->find(1)->getModel()->toArray();
      
    • Check for mass assignment restrictions ($fillable/$guarded).
  3. Migration Conflicts:

    • If publishing migrations fails, manually merge changes or use --force:
      php artisan vendor:publish --tag="migrations" --force
      

Extension Points

  1. Custom Version Logic:

    • Override createVersion() to add pre/post hooks:
      protected function createVersion(array $options = [])
      {
          $this->fireModelEvent('versioning', false);
          parent::createVersion($options);
      }
      
    • Extend Version model to add custom methods (e.g., diff()):
      public function diff(Version $other)
      {
          return array_diff($this->data, $other->data);
      }
      
  2. Version Storage:

    • Use versionableStorage to switch to a custom storage driver (e.g., Redis):
      protected $versionableStorage = 'redis';
      
    • Implement Mpociot\Versionable\Contracts\VersionableStorage for custom backends.
  3. UI Integration:

    • Pair with packages like spatie/laravel-medialibrary for versioned file attachments:
      $post->versions()->where('model_type', Post::class)->with('attachments')->get();
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
anousss007/vigilance
supportpal/eloquent-model
ardenexal/fhir-models
laravel-at/laravel-image-sanitize
romalytar/yammi-audit-log-laravel
ardenexal/fhir-validation
arshaviras/weather-widget
laravel-chronicle/core
sunchayn/nimbus
daikazu/eloquent-salesforce-objects
unseen-codes/chat
romalytar/yammi-jobs-monitoring-laravel
kisame76/filament-db-table-state
nqxcode/laravel-lucene-search
dpfx/laravel-livewire-wizards
workos/workos-php-laravel
sofa/laravel-global-scope
nawasara/auth-primitives
adhocrat-io/arkhe-main
make-dev/orca-harpoon