Essentials
Refining the Timeline
Filters, sorting, and deduplication.
Every refinement is a chainable method on TimelineBuilder. Compose only what you need - each section below covers one concern.
Filtering
Narrow the entries by date window, type, or event. All four filters stack and are cumulative.
$record->timeline()
->between(now()->subMonth(), now()) // CarbonInterface|null on each side
->ofType(['related_model', 'activity_log']) // type allow-list
->exceptType(['custom']) // type deny-list
->ofEvent(['email_sent', 'task_completed']) // event allow-list
->exceptEvent(['draft_saved']); // event deny-list
Sorting
Order the combined stream after sources are merged.
$record->timeline()
->sortByDateDesc(); // default; use sortByDateAsc() for ascending
Deduplication
Entries sharing a dedupKey collapse to the highest sourcePriority (first occurrence wins on ties). Disable entirely, or override the key when the default identity isn't right for your use case.
$record->timeline()
->deduplicate(true) // default: true - pass false to keep every entry
->dedupKeyUsing(fn ($entry) =>
"{$entry->type}:{$entry->event}:{$entry->occurredAt->toDateString()}"
);
Running the query
After filters/sort/dedup are set, one of these methods executes and returns entries.
| Method | Returns |
|---|---|
get() | Collection<int, TimelineEntry> - all entries up to the internal 10 000 cap. |
paginate(?int $perPage, int $page = 1) | LengthAwarePaginator<int, TimelineEntry>. Uses activity-log.default_per_page if $perPage is null. |
count() | int (runs get()). |