Hi, maybe you could try this package: https://github.com/VentureCraft/revisionable
I have used it once in a project and it worked quite well... hope this helps.
Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.
We need to track model changes and output the changes in a user-friendly way. But I'm messing around with relationship and numeric-based attributes which are outputted with accessors.
I get the differences from the Model with a trait named Auditable:
// ...
public function trackChanges() {
$this->track = true;
$this->oldAttributes = $this->getOriginal();
$this->newAttributes = $this->getAttributes();
return $this;
}
/**
* The model's changes.
*
* @return array|null
*/
public function changes()
{
$before = array_diff_assoc($this->oldAttributes, $this->newAttributes);
$after = array_diff_assoc($this->newAttributes, $this->oldAttributes);
if($this->trackChanges()) {
return [
'before' => array_except(
$before, self::$withoutTracking
),
'after' => array_except(
$after, self::$withoutTracking
)
];
}
return null;
}
// ..
But for example, a field "priority" which is stored numeric and resolved via an accessor is unusable in an activity log.
E.g
$activity->changes['before']['priority'] // -> "1", but the user expects a "Standard" or "Express"
So I implemented an accessor for the changes which access a resolver function on the model itself:
Activity Model:
protected $casts = [
'changes' => 'array'
];
/**
* Resolve the changed values to user-friendly values.
*
* @param $changes
* @return mixed
*/
public function getChangesAttribute($changes)
{
$changes = $this->castAttribute('changes', $changes);
if($changes['before']) {
foreach($changes['before'] as $key => $value) {
$changes['before'][$key] = $this->trackable->resolveAttribute($key, $value);
}
}
if($changes['after']) {
foreach($changes['after'] as $key => $value) {
$changes['after'][$key] = $this->trackable->resolveAttribute($key, $value);
}
}
return $changes;
}
Models with activities:
public function resolveAttribute($key, $value) {
$mappings = [
'priority' => $this->getPriorityNameAttribute($value),
];
if(array_key_exists($key, $mappings)) {
return $mappings[$key];
}
return $value;
}
But this solution doesn't feels right... Someone may have a better approach doing this?
Please or to participate in this conversation.