thumbrise/laravel-versioned-model

Versioned model for Laravel.

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/thumbrise/laravel-versioned-model

v1.0.0 2026-01-20 00:56 UTC

This package is not auto-updated.

Last update: 2026-01-20 23:06:05 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads PHPStan Level

A powerful and easy-to-use Laravel package that provides automatic versioning for your Eloquent models. Track changes, create snapshots, compare versions, and revert to previous states with a simple API.

📖 Documentation • 🤝 Contributing • 📋 Changelog • 📜 License

Features

Full Snapshot Versioning - Store complete model state at each version
🔄 Automatic Version Tracking - Track who, when, and what changed
📊 Diff Comparison - Compare any two versions to see what changed
⏮️ Version Rollback - Easily revert to any previous version
📝 Field History - Get complete history of changes for specific fields
🔒 Transaction Safety - All operations wrapped in database transactions
🎯 Selective Tracking - Exclude specific fields from versioning
Performance Optimized - Uses latestOfMany() for efficient queries
🔗 Polymorphic Relations - Track changes made by any model (users, admins, etc.)

Requirements

  • PHP 8.1 or higher
  • Laravel 10.x or 11.x

Installation

You can install the package via composer:

composer require thumbrise/laravel-versioned-model

Publish and run the migrations:

php artisan vendor:publish --tag="versioned-model-migrations"
php artisan migrate

Usage

Basic Setup

Add the HasVersions trait to your model:

use Illuminate\Database\Eloquent\Model;
use Thumbrise\LaravelVersionedModel\Traits\HasVersions;

class Article extends Model
{
    use HasVersions;

    protected $fillable = ['title', 'content', 'status'];
}

Creating Versions

Use the updateVersioned() method instead of regular update() to automatically create a version:

$article = Article::create([
    'title' => 'My First Article',
    'content' => 'Initial content',
    'status' => 'draft'
]);

// Update and create a version snapshot
$article->updateVersioned([
    'title' => 'My Updated Article',
    'content' => 'Updated content'
]);

// Each update creates a new version
$article->updateVersioned(['status' => 'published']);

Retrieving Versions

// Get all versions
$versions = $article->getVersions();

// Get a specific version
$version = $article->getVersion(2);

// Get the latest version
$latestVersion = $article->getLatestVersion();

// Access version data
echo $version->version;        // Version number
echo $version->snapshot;       // Full model snapshot as array
echo $version->created_at;     // When this version was created
echo $version->changer;        // Who made the change (authenticated user)

Comparing Versions

Get the differences between any two versions:

// Compare version 1 and version 2
$diff = $article->getDiff(1, 2);

// Compare version 1 with current state
$diff = $article->getDiff(1, null);

// Compare initial state (before first version) with current state
$diff = $article->getDiff(null, null);

// Result format:
// [
//     'title' => [
//         'old' => 'My First Article',
//         'new' => 'My Updated Article'
//     ],
//     'content' => [
//         'old' => 'Initial content',
//         'new' => 'Updated content'
//     ]
// ]

Reverting to Previous Versions

// Revert to version 1
$article->revertToVersion(1);

// This creates a new version with the old data
echo $article->title; // Back to the original title

Field History

Track the complete history of changes for specific fields:

// Get history for a single field
$history = $article->getFieldHistory('status');

// Result:
// [
//     [
//         'version' => 1,
//         'value' => 'draft',
//         'changed_at' => Carbon instance,
//         'changer' => User model
//     ],
//     [
//         'version' => 2,
//         'value' => 'published',
//         'changed_at' => Carbon instance,
//         'changer' => User model
//     ]
// ]

// Get history for multiple fields
$history = $article->getFieldsHistory(['title', 'status']);

Excluding Fields from Versioning

By default, created_at and updated_at are excluded. You can exclude additional fields:

class Article extends Model
{
    use HasVersions;

    protected static function getExcludedVersionFields(): array
    {
        return ['view_count', 'last_viewed_at'];
    }
}

Custom Changer Resolution

By default, the package tracks changes made by the authenticated user. You can customize this:

class Article extends Model
{
    use HasVersions;

    protected static function resolveChanger(): ?Model
    {
        // Track by admin instead of regular user
        return auth()->guard('admin')->user();
        
        // Or use a different model entirely
        return SystemLog::getCurrentActor();
    }
}

Using Relationships

// Eager load versions
$article = Article::with('versions')->find(1);

// Eager load only the latest version
$article = Article::with('latestVersion')->find(1);

// Query versions
$article->versions()
    ->where('created_at', '>', now()->subDays(7))
    ->get();

Advanced Usage

Manual Version Creation

While updateVersioned() is the recommended approach, you can also create versions manually:

use Thumbrise\LaravelVersionedModel\Models\ModelVersion;

ModelVersion::create([
    'model_type' => $article->getMorphClass(),
    'model_id' => $article->getKey(),
    'changer_type' => auth()->user()?->getMorphClass(),
    'changer_id' => auth()->id(),
    'version' => 1,
    'snapshot' => [
        'title' => 'Custom Title',
        'content' => 'Custom Content'
    ]
]);

Accessing Version Relationships

$version = $article->getVersion(1);

// Get the model this version belongs to
$model = $version->model; // Returns Article instance

// Get who made this change
$user = $version->changer; // Returns User instance (or null)

Database Schema

The package creates a model_versions table with the following structure:

Column Type Description
id bigint Primary key
model_type string Polymorphic type of the versioned model
model_id bigint ID of the versioned model
changer_type string Polymorphic type of who made the change (nullable)
changer_id bigint ID of who made the change (nullable)
version integer Sequential version number
snapshot json/jsonb Complete snapshot of model state
created_at timestamp When this version was created

Indexes:

  • (model_type, model_id, version) - Unique constraint
  • (model_type, model_id) - Query optimization
  • (changer_type, changer_id) - Polymorphic relation

Testing

composer test

Run code quality checks:

composer lint

Fix code style:

composer fmt

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

If you discover a security vulnerability, please email Ruslan Kokoev at ruslan.kokoev.1999@gmail.com. All security vulnerabilities will be promptly addressed.

Credits

License

The MIT License (MIT). Please see License File for more information.