Server API reference (PHP)
Namespace: Elyra\DataGrid\Server. Install via composer require elyra/datagrid-server.
Grid
Factory entry point.
Grid::table(string $table): GridDefinition
GridDefinition
Fluent builder + the security allow-list.
| Method | Returns | Purpose |
|---|---|---|
table(string) (static) |
self | Create a definition. |
key(string $column) |
self | Primary key (tiebreaker, editing, selection). |
connection(?string $name) |
self | config/database.php connection. |
driver(string|GridDriver) |
self | 'elyrasql' (default), 'mysql', 'pgsql', 'clickhouse', 'sqlite' / 'sqlanywhere', or an instance. |
column(string $key, ...) |
self | Declare one column (see below). |
columns(array $keys) |
self | Quick-add columns with defaults. |
searchable(array $keys) |
self | Mark columns for global search. |
respond(GridRequest) |
array | Execute a query → GridResponse array. |
mutate(GridMutation) |
array | create/update/delete → GridMutationResult array. |
export(GridRequest, string $format = 'csv', string $filename = 'export') |
StreamedResponse | Streamed CSV/XLSX. |
authorize(callable $cb) |
self | Gate create/update/delete/export (fn($ability, $ctx): bool). Required before any write/export unless withoutAuthorization() is called. |
withoutAuthorization() |
self | Explicitly opt out of the grid-level write/export gate (route/policy guards it instead). |
limits(GridLimits) |
self | Override request guard rails (take/skip/filter-depth/in-values/export-rows/paste-rows). |
ask(string $query, ?callable $engine = null) |
FilterGroup | Natural-language → validated filter (see below). |
editableColumns() |
GridColumn[] | Columns writable via mutations. |
all() |
array | All declared columns. |
column() signature
public function column(
string $key,
?string $column = null, // real column (default: key)
?string $expression = null, // raw SQL (read-only, not quoted)
bool $sortable = true,
bool $filterable = true,
bool $searchable = false,
?bool $aggregatable = null, // numeric → true
string $type = 'string', // string|number|date|bool
bool $editable = false, // write allow-list
string $editor = 'text', // text|number|select|date|checkbox
string|array|null $rules = null, // Laravel validation
array $options = [], // [['value'=>..,'label'=>..]] for select
): self
GridColumn
Value object describing one column (key, column/expression, sortable,
filterable, searchable, aggregatable, type, editable, editor,
rules, options). isExpression() reports whether it is a raw expression
(read-only).
Drivers — Contracts\GridDriver
Implement to support another dialect. Built-in:
| Class | Notes |
|---|---|
Drivers\ElyraSqlDriver (default) |
FACET() single-pass facets, PERCENTILE/MEDIAN, HYBRID search. |
Drivers\MySqlDriver |
Portability fallback; facets via N GROUP BY; MATCH full-text; no percentile/hybrid. |
Drivers\PostgresDriver |
Double-quoted identifiers; percentile_cont/median; tsvector full-text; no hybrid. |
Drivers\ClickHouseDriver |
OLAP column store; quantile/median; no ESCAPE clause; no hybrid. |
Drivers\SqliteDriver |
SQLite / SQL Anywhere; additive aggregates only; no percentile/hybrid. |
Interface surface: name(), quote(), ref(), aggregateExpr(),
groupBy(), facetQueries(), searchPredicate().
Engine internals
GridEngine— orchestratesrespond(): WHERE (filter + search) → viewport rows / groups / total / aggregates / facets / timing. Grouping runs oneGROUP BYper level so non-additive aggregates (avg/median/percentile/ countDistinct) are correct at every level.exportKeyset()returns the single-pass export plan (selects / table / where / bindings / key).GridMutator— validates and writes create/update/delete (only editable columns, bound values).GridExporter— streams CSV (BOM +;) or a rich XLSX (openspout: bold header band,#,##0.00numeric columns, column widths). Reads in bounded key-range windows (key >= a AND key < b) for constant memory on ElyraSQL.
Natural-language filtering — ask()
ask() translates a plain-language query into a validated FilterGroup using an
LLM whose JSON-schema output is restricted to this grid's declared, filterable
columns and the known operator set. The result is re-validated against the
allow-list (unknown fields/operators dropped) and compiled through the ordinary
FilterCompiler, so it can only ever produce filters the grid already permits;
no row data is sent to the model. Requires the optional laravel/ai package and
a provider key (otherwise a GridException is thrown). The LLM call sits behind
an injectable $engine so it can be stubbed in tests. Full guide:
Natural-language filtering.
Live updates — versionColumn()
->versionColumn('updated_at') declares what stamps the grid's data version.
meta: ['version'] then answers with dataVersion, built from COUNT(*) and
MAX(column) over the request's own filter and search.
The count is what makes a DELETE visible — MAX alone is unchanged by one. It is
still a heuristic: a delete and an insert between two polls landing on the same
maximum give the same stamp, and a write that does not touch the column is
invisible. Full guide: Live updates.
Request limits — GridLimits
GridDefinition::limits() (or config('datagrid.limits')) bounds a single
request: maxTake (1000), maxSkip (100k, caps deep OFFSET), maxFilterDepth
(15), maxInValues (1000), maxPasteRows (500, enforced in mutateBatch() so
every caller is covered — a Livewire paste, your own HTTP endpoint, or
application code), maxFacetValues (1000, also the default when a facet request
omits top), and maxExportRows (null = unlimited). Exceeding a limit throws a
GridException; maxTake/maxSkip/facet top are clamped instead.
Request breadth is bounded too, because depth limits alone leave a
shallow-but-enormous request as a DoS lever on any grid with anonymous read
access: maxFilterConditions (200, across the whole tree), maxGroupLevels (5 —
the engine runs one query per level), maxAggregates (25 per aggregate list),
maxFacets (25, each its own GROUP BY) and maxSearchLength (200 characters).
maxGroupRows (10 000) is different in kind: it bounds a data-dependent
result rather than the request. Grouping a column with a million distinct values
is not abusive, just expensive, so it clamps each grouping level instead of
throwing — and the response carries groupsTruncated: true so a partial grouping
is never presented as the whole picture. Render that as a notice; do not ignore
it.
maxExportRows is only optional for grids that declare a key. A keyless grid
cannot be paginated (no stable order to page by), so its export buffers the whole
result set in memory — with no cap that is an OOM waiting to happen, and
export() refuses rather than risking it. Declare a key, or set an explicit cap.
Column capabilities
The declared flags are the read allow-list, and each is enforced where it
applies: sortable in the sort compiler, filterable in the filter compiler,
aggregatable for aggregate expressions, and editable for writes.
filterable: false also blocks facets and grouping on that column. Both
emit the column's distinct values, which is exactly what the flag withholds — a
facet on a non-filterable column would otherwise dump every value in it.
Exceptions
Exceptions\GridException is thrown for unknown fields, disallowed operations
(non-sortable/filterable/aggregatable), unsupported driver features, bad
operators, exceeded request limits, an unauthorized mutation/export, or an
unavailable assistant — the security guardrail that keeps clients within the
declared schema.
Service provider
GridServiceProvider is auto-discovered. The Livewire package adds
GridLivewireServiceProvider (views, translations, inlined theme).