savannabits/laravel-pagetables
Laravel Pagetables adds clean, paginated data tables to your Laravel apps. Define columns and rows with a fluent API, then render responsive tables with built-in search, sorting, and pagination—ideal for admin panels and dashboards.
Architecture fit. Laravel PageTables is not an architectural layer — it is a controller-adjacent utility that translates DataTables request parameters (draw, start, length, search[value], order[i][column]) into a Laravel paginator call and returns the JSON envelope. It sits between a route and an Eloquent/Query Builder instance and holds no opinion on authorization, tenancy, filtering, or transformation. That thinness is the whole product: it removes the recurring boilerplate of "connect a DataTables grid to a paginator," which is genuinely repeated work on every back-office surface (user lists, order tables, audit logs, subscription rosters). It is a fit for classic Laravel MVC admin panels rendered with Blade. It is a poor fit for SPA/Inertia/Livewire front ends, and no fit at all where the table layer needs to be a differentiator (saved views, per-user column config, exports, aggregations).
Integration feasibility. Mechanically easy and well within a sprint. composer require pinned at 1.1.0, one JSON route per resource, one pagetable class per resource holding a column map, and a standard serverSide: true DataTables config. Realistic estimate: 1–2 engineer-days for the first table including tests, hours for each subsequent one, assuming a BasePagetable is extracted early. The hard part is not installation — it is the decisions the package deliberately leaves to us.
Technical risk. The dominant risk is not code quality; it is ownership and compatibility. The last release (2022-06-13) was a no-op beyond widening constraints for Laravel 9. Laravel 10/11/12 are unclaimed, so installation fails on constraints and every framework major re-opens the same patch. Three stars and zero dependents mean no downstream discovery of bugs — we will be the first to hit them.
| Risk | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Laravel 10+ incompatibility | High | Low–Med | Fork/vendor, widen illuminate/*, document in ADR |
| Request-driven ORDER BY / column injection | Unknown — must verify in src/ |
High | Whitelist through our column map; abort(422) on unknown keys |
| Tenancy/authorization leak (unscoped query) | Medium | Severe | Scope the builder before the pagetable call; test with a second tenant |
recordsTotal vs recordsFiltered wrong |
Medium | Medium | Feature-test that recordsFiltered changes with search[value] |
length=-1 ("All") → error or one-row page |
Medium | Low | Clamp per-page to a sane maximum (e.g. 100) in our base class |
| N+1 amplifies per page of results | Medium | Medium | with()/withCount() and select() computed values |
Key questions to answer before any commitment.
src/ whitelist column names for ordering and searching, or does it trust columns[i][data]? (If the latter, that is a fork-or-skip signal.)draw verbatim and emit both totals from distinct queries?Stack fit. Adopt only where all of the following hold: Laravel 9 today, Blade-rendered internal admin surface, jQuery DataTables front end, low-traffic/internal-only audience, and no requirement for column-level search, faceted filters, exports, or cross-model custom transformers. If any of those fail — especially Laravel 10+ — the fit collapses and the calculus shifts to yajra/laravel-datatables (maintained, larger surface) or an in-house ~200-line layer.
Migration path. This is not a migration; it is a greenfield addition to specific endpoints. Design for reversibility: keep the column map as a data structure owned by us, put all package interaction behind one BasePagetable, and constrain the package to a single class in the codebase. The exit path — swapping the response layer to yajra or to our own implementation — should then be a one-class change plus regression tests on the JSON contract, not a refactor.
Compatibility. Pin savannabits/laravel-pagetables:1.1.0 exactly. Verify PHP version constraints against our runtime. If we must widen illuminate/* for L10+, treat that as a permanent, tracked patch — record it in composer.json comments/ADR so future framework upgrades diff composer.json only and never require re-auditing src/.
Sequencing.
src/ end-to-end and answer the five key questions above. This is the cheapest gate in the whole plan.yajra. The PM position should be that copying ~200 lines of MIT code we then own is often cheaper than a dependency we must own anyway — same maintenance tax, one less upgrade surprise.curl the endpoint for the raw JSON envelope, then a feature test asserting draw, recordsTotal, recordsFiltered, and that filtering changes the filtered count.BasePagetable: column whitelist, per-page clamp, defaultSort, date-range filters, and the compatibility shim all live here.Maintenance. Treat this as vendored code, not a dependency. Budget a standing maintenance line: roughly 0.5–1 engineer-day per Laravel major for constraint bumps and smoke tests, plus a light annual read of src/ for anything security-relevant. Upstream has demonstrated occasional constraint bumps rather than active development, so assume zero future feature work and plan accordingly.
Support. Community-only. No SLA, no security-response posture, no expectation of issue triage. Three stars and zero dependents mean there is no informal user community to lean on. Internal ownership is mandatory and should be named in the ADR — this is a product-ownership decision about who pays the tax, not a purely engineering one.
Scaling. Adequate for internal admin volumes because pagination is server-side and each request is query-bounded. The scaling risks are all ours, not the package's: N+1 accessors firing per page of rows, unindexed LIKE %term% over joined tables, and unbounded length allowing a client to request hundreds of thousands of rows. Cap per-page (≤100), index searchable columns, and prefer select()/selectRaw() over accessors in the payload. Postgres vs. MySQL collation differences on LIKE are a portability landmine if the org runs both.
Failure modes to instrument. In rough order of severity:
draw not echoed → "Invalid JSON response" with the grid stuck on "Processing…"; classic misdiagnosis as an adapter failure.users.name, not name).length=-1 or accidental export attempts outside the adapter.Monitoring should be minimal but explicit: log slow table endpoints, alert on 5xx from table routes, and track query count per request to catch N+1 regressions early.
Ramp-up. Low. One engineer reads the source in an hour; a second engineer needs only the ADR, the BasePagetable, and the column-map convention — not the README. Onboarding material should be internal documentation, because the upstream docs are thin and the method names may not match them. The realistic organizational risk is not a steep learning curve but divergence: without a shared base class and a documented column-map pattern, each team will hand-roll its own variant and the "savings" evaporate.
How can I help you explore Laravel packages today?