Skip to main content

Diff Engine

Chronicle can attach structured change diffs to an entry.

This is useful when you want audit events to describe not just that something changed, but exactly what changed.

Diff structure

A diff is stored as JSON with field names as keys:

{
"amount": {
"old": 1000,
"new": 500
}
}

Manual diffs

You can supply a full diff directly:

Chronicle::record()
->actor($user)
->action('order.updated')
->subject($order)
->diff([
'amount' => [
'old' => 1000,
'new' => 500,
],
])
->commit();

Chronicle normalizes the diff keys and ensures each change node has old and new values.

Single-field changes

For simple cases, use change():

Chronicle::record()
->actor($user)
->action('order.status_changed')
->subject($order)
->change('status', 'pending', 'paid')
->commit();

Eloquent model diffs

Chronicle can derive diffs from a model’s dirty attributes:

$order->status = 'paid';

Chronicle::record()
->actor($user)
->action('order.updated')
->subject($order)
->modelDiff($order)
->commit();

Alias:

->modelChanges($order)

Ignored fields

The builder ignores these fields by default when generating model diffs:

  • created_at
  • updated_at

That keeps routine timestamp churn out of the audit payload.

When to use diffs

Diffs are a good fit for:

  • status transitions
  • mutable business records
  • approval flows
  • administrative changes

They are less useful when the important event is the action itself rather than a before/after comparison.