A Laravel package for automatic model versioning with selective attribute tracking and restore capabilities. Track changes to your Eloquent models with complete version history, restore to previous versions, and maintain a complete audit trail.
- 🚀 Automatic Versioning: Automatically create versions when models are created or updated
- 🎯 Selective Versioning: Choose which attributes to version using
versionableAttributesornonVersionableAttributes - ⏪ Version Restoration: Restore models to any previous version with a single method call
- 👤 User Tracking: Automatically track which user created each version
- đź’¬ Version Comments: Add comments to versions for better change tracking
- ⚙️ Configurable: Extensive configuration options to fit your needs
- đź§Ş Well Tested: Comprehensive test suite with 100% code coverage
- đź”’ Temporary Disabling: Disable versioning temporarily when needed
You can install the package via composer:
composer require act-training/laravel-model-versionsPublish and run the migrations:
php artisan vendor:publish --provider="ActTraining\LaravelModelVersions\LaravelModelVersionsServiceProvider" --tag="model-versions-migrations"
php artisan migrateYou can publish the config file with:
php artisan vendor:publish --provider="ActTraining\LaravelModelVersions\LaravelModelVersionsServiceProvider" --tag="model-versions-config"Add the HasVersions trait to your model:
<?php
use ActTraining\LaravelModelVersions\HasVersions;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasVersions;
protected $fillable = ['title', 'content', 'status'];
}That's it! Your model will now automatically create versions when created or updated:
$post = Post::create([
'title' => 'My First Post',
'content' => 'This is the content...',
'status' => 'draft'
]);
// Automatically creates version 1
$post->update(['title' => 'My Updated Post']);
// Automatically creates version 2
// Get all versions
$versions = $post->versions; // Collection of Version models
// Get current version number
$currentVersion = $post->getCurrentVersionNumber(); // 2
// Get specific version data
$version1Data = $post->getVersionData(1);
// Returns: ['title' => 'My First Post', 'content' => 'This is the content...', 'status' => 'draft']
// Restore to previous version
$post->restoreToVersion(1);
// Post is now back to original state, creates version 3 with restore infoOnly version specific attributes:
class Post extends Model
{
use HasVersions;
protected $fillable = ['title', 'content', 'view_count', 'status'];
// Only version title and content
protected $versionableAttributes = ['title', 'content'];
}Version all attributes except specified ones:
class Post extends Model
{
use HasVersions;
protected $fillable = ['title', 'content', 'view_count', 'last_viewed_at'];
// Version everything except view tracking fields
protected $nonVersionableAttributes = ['view_count', 'last_viewed_at'];
}Create versions manually with optional comments:
$post = Post::create(['title' => 'Draft Post']);
// Create version with comment
$version = $post->createVersion('Initial draft created');
$post->update(['status' => 'published']);
$post->createVersion('Published the post');Restore to any previous version:
// Restore to version 2
$success = $post->restoreToVersion(2);
// Restore with custom comment
$success = $post->restoreToVersion(2, 'Reverted due to content issue');
// Check if restore was successful
if ($success) {
echo "Restored successfully!";
} else {
echo "Version not found!";
}Disable versioning for specific operations:
$post->withoutVersioning(function () use ($post) {
$post->update(['view_count' => $post->view_count + 1]);
$post->update(['last_viewed_at' => now()]);
// No versions created for these updates
});
// Or disable for a single operation
$post->versioningDisabled = true;
$post->update(['view_count' => 100]);
$post->versioningDisabled = false;// Get all versions (ordered by version number descending)
$versions = $post->versions;
// Get current version
$currentVersion = $post->getCurrentVersion();
// Get specific version
$version = $post->getVersion(3);
// Get version data without loading the version model
$data = $post->getVersionData(3);
// Check current version number
$versionNumber = $post->getCurrentVersionNumber();
// Check if model has versionable changes
if ($post->hasVersionableChanges()) {
$post->createVersion('Manual save');
}// Get the version creator (User model)
$version = $post->getVersion(1);
$creator = $version->creator; // User model or null
// Get the versionable model from version
$originalPost = $version->versionable; // Post model
// Access version data
$versionData = $version->data; // Array of model attributes
$versionNumber = $version->version_number; // Integer
$comment = $version->comment; // String or null
$createdAt = $version->created_at; // Carbon instanceThe package comes with sensible defaults, but you can customize behavior in config/model-versions.php:
return [
// Database table name for versions
'table_name' => env('MODEL_VERSIONS_TABLE', 'versions'),
// User model for created_by relationships
'user_model' => env('MODEL_VERSIONS_USER_MODEL', 'App\\Models\\User'),
// Custom version model (if you need to extend it)
'version_model' => ActTraining\LaravelModelVersions\Models\Version::class,
// Auto-versioning settings
'auto_version_on_create' => env('MODEL_VERSIONS_AUTO_CREATE', true),
'auto_version_on_update' => env('MODEL_VERSIONS_AUTO_UPDATE', true),
// Create version when restoring
'create_version_on_restore' => env('MODEL_VERSIONS_VERSION_ON_RESTORE', true),
// Default attributes excluded from versioning
'default_non_versionable_attributes' => [
'id', 'created_at', 'updated_at', 'deleted_at'
],
];You can configure the package using environment variables:
MODEL_VERSIONS_TABLE=custom_versions
MODEL_VERSIONS_USER_MODEL=App\Models\CustomUser
MODEL_VERSIONS_AUTO_CREATE=true
MODEL_VERSIONS_AUTO_UPDATE=true
MODEL_VERSIONS_VERSION_ON_RESTORE=falseCreate a custom version model to add additional functionality:
<?php
namespace App\Models;
use ActTraining\LaravelModelVersions\Models\Version as BaseVersion;
class CustomVersion extends BaseVersion
{
protected $fillable = [
...parent::getFillable(),
'custom_field',
];
public function customMethod()
{
// Your custom logic
}
}Then update your config:
// config/model-versions.php
'version_model' => App\Models\CustomVersion::class,You can disable auto-versioning globally and create versions manually:
// config/model-versions.php
'auto_version_on_create' => false,
'auto_version_on_update' => false,$post = Post::create(['title' => 'New Post']);
// No version created automatically
$post->createVersion('Initial creation');
// Manual version creationFor models with frequent updates, consider:
- Selective versioning: Only version important attributes
- Disable auto-versioning: Create versions manually at important milestones
- Cleanup old versions: Implement a cleanup strategy for very old versions
// Example: Only keep last 10 versions
$oldVersions = $post->versions()
->orderBy('version_number', 'desc')
->skip(10)
->get();
$oldVersions->each->delete();composer testPlease see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). Please see License File for more information.