Skip to main content

Query API Documentation

Chronicle exposes two read surfaces:

  • model scopes on Chronicle\Models\Entry
  • a reader service available through Chronicle::reader()

Use the model when you want full Eloquent composition. Use the reader when you want a small, package-level API for common ledger reads.

Entry model scopes

Import the entry model:

use Chronicle\Models\Entry;

forActor(Model $actor)

Filters entries by actor reference.

$entries = Entry::query()
->forActor($user)
->latestFirst()
->get();

Chronicle matches the actor by both class name and primary key.

forSubject(Model $subject)

Filters entries by subject reference.

$entries = Entry::query()
->forSubject($order)
->get();

action(string $action)

Filters entries by action name.

$entries = Entry::query()
->action('order.created')
->get();

correlation(string $id)

Filters entries by an exact correlation id.

$entries = Entry::query()
->correlation($correlationId)
->get();

This is useful when you want the exact set of entries produced within a single Chronicle transaction context.

workflow(string $rootCorrelation)

Filters entries that belong to a correlation tree.

$entries = Entry::query()
->workflow($rootCorrelation)
->latestFirst()
->get();

Use this when nested Chronicle transactions produce hierarchical correlation ids and you want the full workflow trace.

withTag(string $tag)

Filters entries whose tags JSON array contains a single tag.

$entries = Entry::query()
->withTag('orders')
->get();

withTags(array $tags)

Filters entries that contain all requested tags.

$entries = Entry::query()
->withTags(['orders', 'checkout'])
->get();

Chronicle applies one whereJsonContains() clause per tag.

between(CarbonInterface $start, CarbonInterface $end)

Filters entries by time range.

$entries = Entry::query()
->between(now()->subDay(), now())
->latestFirst()
->get();

latestFirst()

Orders results by created_at descending.

$entries = Entry::query()
->action('order.updated')
->latestFirst()
->get();

Cursor pagination

Chronicle includes two cursor-based pagination scopes for large ledgers.

cursorPaginateLedger(int $perPage = 50, ?string $cursor = null)

Returns entries in ledger order by id.

$page = Entry::query()->cursorPaginateLedger(100);

cursorPaginateLatest(int $perPage = 50, ?string $cursor = null)

Returns entries in reverse ledger order by id.

$page = Entry::query()->cursorPaginateLatest(100);

Cursor pagination avoids expensive offset scans and is the right default for large audit datasets.

Streaming

Chronicle includes lazy cursor-based streaming scopes for low-memory processing.

stream()

Streams entries in ledger order.

Entry::query()
->action('export.ready')
->stream()
->each(function (Entry $entry) {
// process entry
});

streamLatest()

Streams entries in reverse ledger order.

$latestActions = Entry::query()
->streamLatest()
->take(50)
->pluck('action');

Reader API

The reader wraps several common queries behind a simpler interface:

use Chronicle\Facades\Chronicle;

$reader = Chronicle::reader();

Available methods:

  • paginate(int $perPage = 50, ?string $cursor = null)
  • stream()
  • forActor(Model $actor)
  • forSubject(Model $subject)
  • action(string $action)
  • correlation(string $id)

Example:

$page = Chronicle::reader()->paginate(100);
$entries = Chronicle::reader()->forSubject($order);
$auditTrail = Chronicle::reader()->correlation($correlationId);
  • Use latestFirst() for operational review screens
  • Use cursor pagination for browsing large ledgers
  • Use streaming for exports, verification helpers, and batch analysis
  • Use withTags() carefully on very large PostgreSQL datasets unless you add JSON-specific indexes

For PostgreSQL-specific indexing strategies, see PostgreSQL JSON Index Documentation.