anonimowybanan / model-diff
Laravel package for tracking and comparing Eloquent model changes
Requires
- php: ^8.1
- illuminate/database: ^10.0 || ^11.0 || ^12.0 || ^13.0
- illuminate/support: ^10.0 || ^11.0 || ^12.0 || ^13.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0|^11.0
- phpunit/phpunit: ^10.0|^11.0
This package is auto-updated.
Last update: 2026-04-17 15:39:39 UTC
README
Track and compare Eloquent model changes with ease. ModelDiff automatically logs field-level modifications, supports batched change tracking, and provides a powerful query API for browsing change history.
Requirements
- PHP 8.1+
- Laravel 10+
Installation
You can install the package via Composer:
composer require anonimowybanan/model-diff
Publish the config file and migration:
php artisan vendor:publish --provider="AnonimowyBanan\ModelDiff\ModelDiffServiceProvider"
Run the migration:
php artisan migrate
Quick Start
Add the TracksChanges trait to any Eloquent model you want to track:
use AnonimowyBanan\ModelDiff\Traits\TracksChanges; class User extends Model { use TracksChanges; }
That's it. Every time the model is updated, changes are automatically logged to the database.
$user = User::create(['name' => 'John', 'email' => 'john@example.com']); $user->update(['name' => 'Jane']); // A change log entry is now stored for the 'name' field
Usage
Comparing Changes
Compare the current dirty state of a model against its persisted state:
$user = User::find(1); $user->name = 'Jane'; $diff = $user->getDiff(); if ($diff->hasChanges()) { $diff->changedFields(); // ['name'] $change = $diff->fieldChange('name'); $change->oldValue; // 'John' $change->newValue; // 'Jane' }
You can also compare two separate model instances:
use AnonimowyBanan\ModelDiff\Facades\ModelDiff; $diff = ModelDiff::compareModels($oldUser, $newUser);
Querying Change History
use AnonimowyBanan\ModelDiff\Facades\ModelDiff; // All changes for a model (newest first) $history = ModelDiff::historyFor($user)->get(); // Changes grouped by batch (all fields changed in a single save) $grouped = ModelDiff::groupedHistoryFor($user);
Query Scopes
The ChangeLog model provides several useful scopes:
use AnonimowyBanan\ModelDiff\Models\ChangeLog; ChangeLog::forModel($user)->get(); // Changes for a specific model ChangeLog::forModel($user)->forField('name')->get(); // Changes for a specific field ChangeLog::byUser($userId)->get(); // Changes made by a specific user ChangeLog::inBatch($batchId)->get(); // Changes from a specific batch
Output Formatting
$diff = $user->getDiff(); // Array format $diff->toArray(); // [['field' => 'name', 'old' => 'John', 'new' => 'Jane']] // Table format (useful for Artisan commands) $diff->toTable(); // [['Field', 'Old Value', 'New Value'], ['name', 'John', 'Jane']]
Manual Logging
If you prefer to log changes manually, disable auto-logging and call logDiff yourself:
config(['model-diff.auto_log' => false]); $diff = app('model-diff')->compare($user); app('model-diff')->logDiff($user, $diff, ['reason' => 'Admin override']);
Batch Tracking
When multiple fields change in a single update(), they share the same batch UUID:
$user->update(['name' => 'Jane', 'email' => 'jane@example.com']); $logs = ChangeLog::forModel($user)->get(); $logs[0]->batch === $logs[1]->batch; // true
Configuration
After publishing, the config file is located at config/model-diff.php.
Global Ignored Fields
Fields that are never tracked across all models:
'ignored_fields' => ['password', 'remember_token', 'updated_at', 'created_at'],
Auto Logging
Toggle automatic change logging on model save:
'auto_log' => true,
User Resolver
Customize how the current user is resolved for change attribution:
'user_resolver' => fn () => auth()->id(), // Or use a callable class 'user_resolver' => App\Services\CustomUserResolver::class,
Per-Model Configuration
Define tracked or ignored fields on a per-model basis:
'models' => [ // Whitelist: track ONLY these fields App\Models\User::class => [ 'only' => ['name', 'email'], ], // Blacklist: ignore these fields (in addition to global ignored) App\Models\Post::class => [ 'ignored' => ['view_count', 'cache_key'], ], ],
Trait-Level Overrides
Override tracked or ignored fields directly on the model:
class User extends Model { use TracksChanges; public function getTrackedFields(): ?array { return ['name', 'email']; // Only track these fields } public function getIgnoredFields(): array { return ['api_token']; // Ignore in addition to global list } }
Custom Table Name
Change the database table used for storing change logs:
'table_name' => 'model_diff_change_logs',
Testing
composer test
License
The MIT License (MIT). Please see License File for more information.