tally/laravel backs a grid from a Laravel app. A grid class declares the columns, Route::tally() exposes it, and the adapter compiles filter, sort, group, aggregate, facet and page requests into SQL. A client connects with connectHttpSource exactly as it does to tally-server.
Grid
A grid extendsTally\Laravel\Grid and implements query() and columns().
app/Grids/AccountsGrid.php
createSchema in tally-core and are sent to the client unchanged. Grids are resolved from the container, so the constructor can take dependencies.
Every method below can be overridden:
Flags
A flag filter such asis:paying calls the closure of the same name with the query builder.
FlaggedAccountsGrid.php
Routes
Route::tally($uri, $grid) registers the four protocol routes under $uri. Wrap it in middleware like any other route.
routes/web.php
The schema route reports
live: false, so the client opens no event stream. Every request is validated against the tally-protocol JSON Schema and then against the grid’s columns before any SQL runs. Invalid requests return 400 with { code, message, details }, where every detail names the path of a problem. See Protocol.
Semantics
The adapter passes thetally-protocol conformance suite on SQLite, Postgres and MySQL, so results match the in-memory engine:
- Empty means
NULL, and for text columns also''. Empty values sort last in both directions and never match comparisons, exceptneq,notContainsandnotIn, which keep them. - Text comparisons are case insensitive through
lower(). SQLite folds ASCII only. - Enum columns sort and facet in option order.
- Date groups bucket to
YYYY-MM-DD, ISO Monday weeks,YYYY-MM,YYYY-QnorYYYY. - Search matches the stored value of searchable columns, not the formatted text.
- Group order follows the sort on the group column when there is one, otherwise option or natural order, newest dates first.
- Empty values form one group,
NULLand''alike, which sorts last and pages like any other group.
Dialects
ADialect supplies the functions SQL engines disagree on: lower, text, float, floor, dayNumber, dateBucket, plus encode and decode for values crossing the driver. The dialect is picked from the connection driver: sqlite, pgsql, mysql and mariadb. Extend Tally\Laravel\Sql\Dialect for another engine and return it from dialect().
Mutations
Mutations run in one database transaction and return the canonical rows read back from the database. Deletes and updates are scoped byquery(), so a grid never writes to rows it cannot read.
When the transaction fails, every mutation is rolled back, the exception is reported, and the response is { accepted: false, errors: [{ message: 'Mutation failed' }] }.
Without HTTP
The adapter runs without a route, for jobs, commands or your own controllers.Testing
Test a grid with Laravel’s HTTP test helpers. AfterRoute::tally('/api/accounts', AccountsGrid::class), a request that does not fit the columns answers with every problem:
tests/Feature/AccountsGridTest.php
tally-protocol conformance cases. The JSON Schema files in resources/schemas and the fixtures in tests/Fixtures/conformance.json are built from tally-protocol, so the PHP adapter is checked against the same cases and expected results as the TypeScript adapters, on every supported database: