Search
Objects support full-text search via Laravel Scout. Add theSearchable trait and implement toSearchableArray() to define what gets indexed. Objects that implement DomainObjectContract can also opt into the application-wide global search bar.
Basic Setup
allowGlobalSearch() controls whether the model appears in the application search bar. Gate it behind a permission so users only see objects they can access. Omit the method (or return false) to keep the model out of global search entirely.
The #[SearchUsingPrefix] attribute enables prefix matching — “Acm” matches “Acme Corp” but not “Company Acme”.
With the database Scout driver, no indexing happens automatically. Only include actual database columns in
toSearchableArray(), not computed values unless they exist as real columns (like computed_text_id).Searching Joined Columns
- Search and display
- Search only
Override Use table-prefixed keys (e.g.,
loadDomainObject() when you need joined data for both search and display:'users.name') in toSearchableArray() when referencing joined columns.Search Behavior
By default, all fields intoSearchableArray() use WHERE LIKE %search% OR ... (contains matching). You can configure more specific behavior per field.
Prefix Search
Use#[SearchUsingPrefix] for fields that should match from the start — “123” matches “12345” but not “51234”:
Full-Text Search
For better performance on long text columns (descriptions, articles, comments), add a full-text index:Computed Text Columns for ID Search
Power Joins
joinRelationship() comes from the eloquent-power-joins package.
leftJoinRelationship() when the relationship might not exist.
Activity Log
The activity log provides an audit trail for domain objects and supports inline comments. It is powered by Spatie activitylog and surfaced in theAppPage sidebar and the system activity log index.
Enable on a Page
Automatic Logging
Models that implementDomainObjectContract and use the HasDomainObject trait get automatic activity logging with these defaults:
- Logs all attributes.
- Excludes
created_at,updated_at,deleted_at, andremember_token. - Only logs dirty (changed) attributes.
- Skips empty logs.
Attribute Labels
The activity log resolves attribute labels in the following order:- Model-specific override via
formatActivityLogAttributeLabel()— for labels specific to a model. - Global attributes from
lang/{locale}/validation.php— for common attributes used across multiple models. - Auto-generated from the attribute key — as a final fallback.
lang/{locale}/validation.php so they stay consistent between validation messages and the activity log:
formatActivityLogAttributeLabel():
Schema property formatting
When a model has a schema, each property controls how its value is formatted in the activity log viatoActivityLogValue():
- Default (string, number, date, enum, etc.): the value is formatted using the property’s
toData()output (e.g. enum label, ISO date). - ObjectProperty: the value (a related model or id) is formatted as a linked object reference so the log can resolve and display the related record.
- RichTextProperty: the value is formatted as rich text so the log shows a “Show difference” modal instead of raw HTML.
formatActivityLogAttributeValue() when you want formatting that is not covered by the schema (e.g. custom labels for enum values or non-schema attributes).
Custom Values
OverrideformatActivityLogAttributeValue() for custom value formatting when the schema does not apply or you need different behaviour:
Custom Descriptions
OverrideformatActivityLogDescription() to customize how activity entries are described:
Rich Text Content
Properties containing HTML rich text should useActivityValueFormatter::richText() so the activity log renders them properly instead of showing raw HTML:
ActivityValueFormatter Reference
| Method | Description |
|---|---|
objectReference($modelClass, $value, $column = 'id') | Linked object reference. Supports single values and arrays. |
enum($enumClass, $value) | Formats enums using label() when available. |
date($value) / datetime($value) | Formats dates. |
richText($value) | Formats HTML rich text for modal diff view. |
auto($model, $key, $value) | Auto-formats based on model casts. |
Comments
When the activity log is enabled on anAppPage, a comment form is displayed above the feed unless comments are disabled.
- Comments are stored as activity entries with
log_name = commentsandproperties.message. - The comment endpoint uses a signed URL.
- Users can delete their own comments.
State history
State history records when a model’s stateful attributes (e.g.status, phase) change, who caused the change, and how long each state lasted. Use it for audit trails, time-in-state reporting, and “path” queries (e.g. “orders that went through draft|pending|approved”).
When to use it
- Status or workflow fields — track
status,phase,stage, or any enum/string that represents a step in a process. - Audit trail — see who moved an order from “pending” to “approved” and when.
- Duration and path — each entry stores
duration(milliseconds in that state) and apath(e.g.draft|pending|approved) so you can report on time spent per state or filter by path.
Enable tracking on a model
Use theTracksStateHistory trait and declare which attributes to track:
created and updated. Enum and Carbon values are normalized to strings; the authenticated user is stored as the causer unless you pass one explicitly.
Reading state history
| What you need | How to get it |
|---|---|
| All state history for the model | $order->stateHistories() |
| History for one attribute, chronological | $order->stateHistoryFor('status') |
Current path string (e.g. draft|pending) | $order->currentStatePath('status') |
| Current path as array of steps | $order->currentStatePathSteps('status') |
StateHistory entry has: old_state, new_state, path, duration (ms), is_latest, causer (morph to user/model), and timestamps.
Querying StateHistory
Use theStateHistory model when you need to query across many records:
Manual recording
When you change state outside normal Eloquentcreate/update (e.g. in a job or console command), record the transition yourself:
StateHistory entry, or null when both old and new state are null (no change).