Skip to content

Source API

Types below live under InertiaX\Table\Sources; the source interface is InertiaX\Table\Sources\Contracts\TableSource. Start with Custom sources to build a repository adapter or Connect an API source for HTTP request translation.

interface TableSource
{
public function fetch(TableQuery $query): TableResult;
}

Supply an instance, implementing class name, container-bound interface extending TableSource, or deferred closure returning one through data(). Source instances are reusable strategies and are not cloned; do not retain request state on a shared instance. Class resolution and options happen before state validation. fetch() runs after validation. The explicit unavailable-page signal shows a table-local problem without retrying the source. Existing model class names retain their Eloquent meaning; an ambiguous model/source class fails.

CallbackSource::make(Closure $callback): CallbackSource creates an immutable definition. The callback returns TableResult and selects TableQuery $query, string $tableId, and explicitly bound services by supported parameter name/type. Optional defaults remain available for ordinary unresolved dependencies. Required unresolved or ambiguous dependencies fail contextually.

A bare data(fn () => ...) resolves a source earlier; it cannot request TableQuery or return a TableResult/paginator directly. Use the wrapper for retrieval.

TableQuery is final and readonly. InertiaX constructs it from validated table state. Its public constructor takes the following named arguments; defaults are useful for adapter tests:

tableId Link to tableId
Type string
Default / meaning Required table ID
rowKey Link to rowKey
Type string
Default / meaning id; declared record identity field
paginationEnabled Link to paginationEnabled
Type bool
Default / meaning true
page Link to page
Type int
Default / meaning 1; positive, one-based page
pageSize Link to pageSize
Type int
Default / meaning 10; positive and allowed by the table
sorting Link to sorting
Type list<array{key: string, direction: 'asc'|'desc'}>
Default / meaning []; requested precedence, without an implicit tie-breaker
filters Link to filters
Type Root group
Default / meaning ['operator' => 'and', 'conditions' => []]
Type string
Default / meaning ''; trimmed validated term
searchableColumns Link to searchableColumns
Type list<string>
Default / meaning []; declared eligible fields, not arbitrary client fields

offset(): int returns (page - 1) * pageSize, or 0 when pagination is disabled. Overflow fails before retrieval. effectiveSorting(): array returns requested sorting with one ascending row-key tie-breaker if an active sort omits it. Empty sorting stays empty.

Each filter group is { operator: 'and'|'or', conditions: [...] }; a child is another group or { key, clause, value }. key is the filter’s key, and value matches the clause’s input: null when it takes none. Repeated fields, nested AND/OR groups, order, native scalar types, and range/list values are preserved. Values are detached JSON data. Namespaced custom clauses remain identifiers for the application to translate. Built-in clause meanings should stay consistent. See Filter groups for bounds and basic/advanced modes.

Constructing a query manually does not perform the Table’s full definition/state validation. Sources should still map trusted fields and reject provider limitations before retrieval. Selection, column visibility, PHP callbacks, provider URLs, and credentials are not query fields.

Only columns with type('text') can be searchable with a custom source. A TextColumn changed to a custom type cannot be searchable on this path. The source implements matching across searchableColumns using search; searchUsing() callbacks are rejected. Document the provider’s comparison and collation semantics.

new TableResult(iterable $rows, int $total) Link to new TableResult(iterable $rows, int $total)
Snapshot finite raw array/object records into a list; exact nonnegative JavaScript-safe total
TableResult::all(iterable $rows) Link to TableResult::all
Derive total from all records for unpaginated retrieval
TableResult::fromPaginator(LengthAwarePaginator $paginator, TableQuery $query) Link to TableResult::fromPaginator
Require enabled pagination and matching page/page size; use items and total

The public readonly properties are array $rows and int $total. Record generics preserve array or object types; readonly does not deep-freeze application record objects. Row keys and column cell serialization are still validated by the normal pipeline.

  • total is the exact number of matching records before pagination, not the length of the page.
  • Return min(pageSize, max(0, total - offset)) records. Short pages with a larger claimed total and excess rows fail validation.
  • lastPage is derived from the total and page size, with a minimum of 1. Empty results require total 0, no rows, and page 1. Out-of-range pages fail without clamping or a second fetch.
  • With pagination(false), return every matching record and its count. TableResult::all($rows) derives that count. A source may reject unpaginated retrieval explicitly.
  • Totals must be nonnegative JavaScript-safe integers. Rows must be finite array/object records; missing, duplicate, or invalid identities fail normal row-key validation.

InertiaX does not filter, search, sort, or slice returned rows again. Paginator URLs are ignored; the frontend owns navigation. Unknown totals, simple/cursor paginators, and infinite streams are unsupported. Application code must keep the provider’s page and count consistent.

Input modifyUsing() and validateUsing() callbacks run before fetch; their resulting values must remain JSON data. Values follow Clause validation. For an application comparison, use Clause::make('acme/in-region')->validateUsing(...) and translate that key in your source. It needs no SQL callback. A filter’s column() does not reach the source: map each filter key to the provider’s field there.

Direct Clause/Filter implementations retain definition and value-validation behavior. Their Eloquent/Collection apply() methods are not invoked. Built-in Boolean storage maps are likewise not applied to custom results; sources receive the validated Boolean input.

DataSourceKind::Custom (under Table\Enums) declares custom-source select option support. options(Closure, DataSourceKind::Custom) supplies the filter, request, resolved TableSource or CallbackSource, and bound services. Options precede final state validation; named baseSource, query, and state requests fail with guidance. An options callback may make its own metadata request, so invalid table state does not guarantee zero metadata I/O. There is no faceting contract. Explicit option arrays also work. See the API source example.

An options closure typed to a particular source class requires that resolved class. If a per-use data() override supplies a CallbackSource instead, replace the closure with explicit options or a compatible callback.

autoColumns(), model-derived columns, and select autoOptions() require local model/data information and fail for custom sources. Automatic filters derived from explicit columns remain available, without the is-set and is-not-set clauses that nullable database columns get; advertise only operations the source implements.

sortUsing(), searchUsing(), applyClauseUsing(), and clause applyUsing() conflict with custom retrieval even if they declare DataSourceKind::Custom support. Put those operations in the source.

Invalid table state fails before row fetch. Provider exceptions retain source/table/phase context and their original cause. Laravel authentication, authorization, not-found, HTTP, and validation exceptions preserve their response semantics through both source forms.

Server execution fetches once; refreshing starts a new execution. TablePageUnavailableException produces a table-local problem for Inertia URL pagination without an automatic retry. Caches and remote retries belong to application code. Browser cancellation does not guarantee remote cancellation.

See Callback context for injectable arguments and resolution rules.

View Markdown