All packages
Attributes for Laravel
History & audit trail
Enable attributes.history.enabled to record an old→new revision whenever an attribute is attached, updated — value or meta — or detached. It is off by default — no revision rows are written until you opt in:
ATTRIBUTES_HISTORY=trueuse RoundlyConsulting\Attributes\Facades\Attributes;
Attributes::for($product)->history(); // Collection<AttributeRevision> — newest first
Attributes::for($product)->history('color'); // filtered by attribute name
$product->history('color'); // model shorthand
$product->attributeHistory(); // the underlying MorphMany relationWhat a revision holds
| Column | Holds |
|---|---|
type | RevisionType — attached, updated or detached. |
name | The attribute name. |
old_value, new_value | Storage strings before and after — ciphertext for encrypted attributes. |
old_value_type, new_value_type | AttributeType values before and after. |
old_meta, new_meta | Metadata before and after, cast to Collections. |
owner_type, owner_id | The owning model (owner() morph-to relation). |
created_at | When the change happened. |
use RoundlyConsulting\Attributes\Enums\RevisionType;
foreach ($product->history('color') as $revision) {
$revision->type; // RevisionType::Attached | Updated | Detached
$revision->old_value; // previous storage string, or null
$revision->new_value; // new storage string, or null on detach
$revision->old_value_type; // 'string', 'integer', ...
$revision->new_value_type;
$revision->old_meta; // Collection|null
$revision->new_meta;
$revision->created_at;
$revision->type->is(RevisionType::Updated); // bool
}
$product->attributeHistory('color')->limit(10)->get(); // a relation — query it freelyWhat is recorded
- attached — the first write of a name through any writer, meta() included, and re-attaching a detached name.
- updated — writing a value or meta to a name that is attached, including values re-sent by a sync.
- detached — every removal of an attached name: forget(), forgetExcept() and the extras sync() drops, plus their model methods.
Pruning revisions
attributes:prune only touches attributes — pruning the revisions table is left to your application, for example with a scheduled query:
// routes/console.php
use Illuminate\Support\Facades\Schedule;
use RoundlyConsulting\Attributes\Models\AttributeRevision;
Schedule::call(function (): void {
AttributeRevision::query()
->where('created_at', '<', now()->subYear())
->forceDelete();
})->weekly();Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.