Elyra
Elyra The coding agent e The native code editor Elyra Grove Native local development environment Askr The real server for Laravel & PHP Elyra Framework Rust + Svelte 5 framework for desktop apps Elyra Conductor Local project conductor Elyra SQL Server MySQL-compatible SQL server in Rust Elyra Félagi Agents as teammates on one board Elyra SQL Client Native desktop SQL workbench Elyra SQL Anywhere Replication-ready SQL engine Elyra Sjá SEO & GEO workspace for macOS Elyra DataGrid Server-driven data grid for Laravel
Elyra
Server API reference (PHP)

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 — orchestrates respond(): WHERE (filter + search) → viewport rows / groups / total / aggregates / facets / timing. Grouping runs one GROUP BY per 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.00 numeric 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).