keshavmansure / laravel-chronicle
Lightweight Laravel package that automatically tracks Eloquent model changes.
Package info
github.com/keshavmansure/laravel-chronicle
pkg:composer/keshavmansure/laravel-chronicle
Requires
- php: ^8.2
- illuminate/database: ^11.0|^12.0
- illuminate/http: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0
- phpunit/phpunit: ^11.0
This package is auto-updated.
Last update: 2026-07-31 19:36:52 UTC
README
Lightweight Eloquent change tracker for Laravel.
Add one trait to a model — creates, updates, and deletes are recorded automatically with before/after snapshots, optional request metadata, and almost zero configuration.
Package: keshavmansure/laravel-chronicle
Requirements
- PHP 8.2+
- Laravel 11 or 12
Installation
composer require keshavmansure/laravel-chronicle
Publish the config (optional):
php artisan vendor:publish --tag=chronicle-config
Publish the migration (optional — migrations also auto-load):
php artisan vendor:publish --tag=chronicle-migrations
Or publish both:
php artisan vendor:publish --tag=chronicle
Run migrations:
php artisan migrate
If you publish the migration and leave package auto-loading enabled, keep only one copy so the table is not created twice.
Quick start
use KeshavMansure\Chronicle\Traits\HasChronicle; use Illuminate\Database\Eloquent\Model; class Post extends Model { use HasChronicle; }
That's it.
$post = Post::create(['title' => 'Hello', 'body' => 'World']); $post->update(['title' => 'Updated']); $post->delete();
Reading history
use KeshavMansure\Chronicle\Models\ChronicleEntry; ChronicleEntry::query() ->where('model_type', Post::class) ->where('model_id', $post->id) ->latest() ->get(); // Via the model relationship $post->chronicleEntries;
Excluding attributes
Global — config/chronicle.php:
'exclude' => [ 'password', 'remember_token', ],
Per model — merged with the global list:
class User extends Model { use HasChronicle; protected array $chronicleExclude = [ 'api_token', 'two_factor_secret', ]; }
Per-model overrides
class User extends Model { use HasChronicle; protected array $chronicleEvents = ['created', 'updated']; protected array $chronicleExclude = ['api_token']; protected array $chronicleOnly = ['name', 'email']; protected string $chronicleSnapshot = 'changes'; // or "full" }
| Property | Purpose |
|---|---|
$chronicleEvents |
Which events to track on this model |
$chronicleExclude |
Extra attributes to omit (merged with config) |
$chronicleOnly |
If set, only these attributes are stored |
$chronicleSnapshot |
full or changes |
Configuration
Publish to config/chronicle.php. Only enabled reads from the environment (CHRONICLE_ENABLED); everything else is config-only.
| Option | Default | Description |
|---|---|---|
enabled |
true (CHRONICLE_ENABLED) |
Globally enable/disable tracking |
connection |
null |
DB connection (app default if null) |
table |
chronicle_entries |
Table name |
snapshot |
full |
full or changes |
events |
created, updated, deleted |
Events to track |
exclude |
password, remember_token |
Attributes never stored |
only |
[] |
When set, only these attributes are stored |
metadata.user_id |
true |
Store authenticated user ID |
metadata.ip |
true |
Store request IP |
metadata.user_agent |
true |
Store user agent |
pretty_print |
false |
Pretty-print JSON snapshots |
What gets stored
| Column | Description |
|---|---|
model_type |
Eloquent morph class |
model_id |
Model primary key |
event |
created, updated, or deleted |
before |
JSON snapshot before the change |
after |
JSON snapshot after the change |
user_id |
Authenticated user (if available) |
ip_address |
Request IP (if available) |
user_agent |
User agent (if available) |
created_at / updated_at |
Timestamps |
Extensibility
Recording goes through the ChronicleRecorder contract. Bind your own implementation to swap storage or queue writes later without changing the public API:
$this->app->singleton( \KeshavMansure\Chronicle\Contracts\ChronicleRecorder::class, \App\Chronicle\QueuedChronicleRecorder::class, );
Testing
composer install
composer test
CI runs on PHP 8.2 / 8.3 against Laravel 11 and 12 (SQLite in-memory).
If your local PHP has no pdo_sqlite:
cp .env.testing.example .env.testing # set DB_* and create the database, then: composer test
Or install SQLite: sudo apt install php8.2-sqlite3
Roadmap
Not in the current version:
- Rollback support
- Diff viewer / dashboard UI
- Soft delete & restore tracking
- Queue support
- Multi-tenant support
- Custom metadata providers
License
MIT © Keshav Mansure