Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/HypathStack/model-scribe/llms.txt

Use this file to discover all available pages before exploring further.

Before any driver touches an audit event, ModelScribeObserver packages all of the relevant data into a LogEntry — a final PHP class whose every property is declared readonly. This immutability guarantee means that once the observer constructs a LogEntry, no driver or downstream code can quietly alter it. Every driver in your pipeline sees exactly the same record that the observer built.

Fields

event
ScribeEvent
required
The Eloquent lifecycle event that triggered this audit record. This is an instance of the ScribeEvent enum, whose string values are created, updated, deleted, restored, retrieved, and custom. Drivers persist the enum’s string value (e.g., the database driver stores $entry->event->value).
logName
string
required
The routing key that determines where this entry is stored. For the database driver, this key is matched against named stores in the config to select the target table and connection. For the file driver, it is written into the log context as log_name. The value comes from resolveLogName() in the observer, which applies the model’s $auditLogName property first, then falls back to the guard_stores mapping in config.
description
string | null
An optional free-text description of the event. When null (the default for observer-generated entries), the file driver synthesises a label from the event and log name: [updated] invoices. You can supply a custom description when calling ModelScribe::log() manually.
subject
Model | null
The Eloquent model instance whose change triggered this entry. The database driver stores subject_type (the fully qualified class name) and subject_id (the model’s primary key). The model instance itself is not serialised into the database — only its class and key are.
causer
Model | null
The authenticated user who performed the action, as resolved by resolveCauser(). Will be null when the event occurs in a context with no authenticated user (e.g., a queue worker, a seeder, or a console command). Like subject, the database driver stores causer_type and causer_id rather than the full model.
properties
array
required
The before-and-after diff for this event. Always contains two keys: old (the attribute values before the change) and attributes (the attribute values after the change). The exact contents differ by event type — see Properties Structure below.
tags
array
A flat array of strings attached to this entry, sourced from the model’s $auditTags property. Tags are useful for filtering and grouping entries across models — for example, ['billing', 'finance']. Defaults to an empty array. The database driver stores this as a JSON column; the file driver includes it in the log context.
url
string | null
The full URL of the HTTP request that triggered the event, captured via Request::fullUrl(). Will be null when capture_request_context is false in config, or when the event occurs outside an HTTP request (e.g., in a queued job or Artisan command).
ipAddress
string | null
The client IP address of the request that triggered the event, captured via Request::ip(). Subject to the same conditions as urlnull when request context capture is disabled or unavailable.
userAgent
string | null
The User-Agent header string of the request, captured via Request::userAgent(). Subject to the same conditions as url and ipAddress.
batchUuid
string
A UUID string that groups related log entries together. When a LogEntry is constructed without an explicit batchUuid, one is auto-generated via Str::uuid(). Passing the same UUID when constructing multiple LogEntry instances manually links those entries as a logical batch — useful when a single user action triggers changes across several models.

Properties Structure

The properties array captures a before/after snapshot of the model’s attributes. Its shape is always { "old": {...}, "attributes": {...} }, but the contents of each key vary by event.

created event

The model has just been inserted. There is no prior state, so old is an empty array. All audited attributes at the time of creation are captured in attributes.
{
  "old": [],
  "attributes": {
    "status": "pending",
    "total": 100
  }
}

updated event

Only the fields that actually changed are recorded. old contains the values those fields held before the save (retrieved via getOriginal()), and attributes contains the new values (from getDirty()). Unchanged fields are omitted entirely, keeping the diff focused.
{
  "old": {
    "status": "pending"
  },
  "attributes": {
    "status": "shipped"
  }
}

deleted event

The model has been removed. There is no meaningful “after” state, so attributes is an empty array. The full attribute snapshot at the time of deletion is stored in old, creating a complete tombstone record.
{
  "old": {
    "status": "shipped",
    "total": 100
  },
  "attributes": []
}

restored event

A soft-deleted model has been restored. This follows the same shape as createdold is empty and the current attributes appear in attributes — because restoration brings the record back into an active state.
{
  "old": [],
  "attributes": {
    "status": "shipped",
    "total": 100,
    "deleted_at": null
  }
}

The withLogName() Method

LogEntry is immutable, so it exposes a single copy-and-modify method: withLogName(). It returns a new LogEntry instance that is identical to the original in every field except logName, which is replaced with the supplied value. All other fields — including batchUuid — are preserved exactly.
$updatedEntry = $entry->withLogName('invoices');
This method is used internally by ModelScribeObserver::handle() after resolveLogName() determines the final routing key, allowing the observer to update the log name without mutating the entry it already constructed:
$this->manager->driver($driverName)->log($entry->withLogName($logName));
batchUuid is auto-generated per event when you let the observer handle logging. If you construct LogEntry instances manually and want to group several entries into the same batch — for example, because one user action updates multiple models — pass the same UUID string to the batchUuid argument of each LogEntry constructor call. Because withLogName() preserves batchUuid, log name re-routing inside the observer never breaks grouping.

Build docs developers (and LLMs) love