Skip to content

AnonimowyBanan/ModelDiff

Repository files navigation

ModelDiff

Latest Version on Packagist Total Downloads License

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.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages