firano/datagrid-bundle
firano/datagrid-bundle is a data grid package for PHP/Laravel apps that helps you build sortable, searchable tables with reusable columns, filters, pagination, and configurable actions, making admin listings and reports faster to implement.
Start by accepting the constraint: this is not a Laravel package. firano/datagrid-bundle is a Symfony 2 bundle (NeutronDataGridBundle) that wires the jqGrid jQuery plugin into a Symfony kernel via DI container, bundle registration, and Twig helpers. There is no service provider, no facade, no Blade integration. composer require will download code that nothing in a Laravel app bootstraps.
So "getting started" means treating the repo as a specification, not a dependency:
composer.json. Clone the repo locally or browse it on GitHub instead.DataGrid/. These two files are the entire usable surface area. Note how page, rows, sidx, sord, _search, and filters are read and translated into a query, and how the response envelope (page, total, records, rows) is assembled.data() action, one Blade view with a <table> and a jQuery/jqGrid init block (or a modern table component — see below).Where to look first, in order:
Resources/public — the bundled jqGrid JS/CSS assets; confirms the jQuery-era front-end this was built for.Resources/config/services.xml — the DI wiring, mostly informative; you will not replicate this in Laravel.First use case: a read-only, paginated, sortable index of one entity (User), rendered in Blade, backed by one Laravel endpoint that returns the jqGrid envelope from Eloquent. Get that end-to-end before touching search, filters, or inline editing — the envelope and the paging math are where the real decisions live.
One grid definition per entity, kept out of the controller. Mirror the bundle's structure: a small class (or a config array / FormRequest-style object) per entity that declares columns, labels, formatters, and the query source. The controller stays thin — it resolves the definition, hands it the request, and returns JSON. This is the one architectural idea worth carrying over wholesale.
The endpoint is the real work. jqGrid sends page, rows, sidx, sord, _search, and optionally filters. Translate them onto an Eloquent builder, never onto hydrated results:
$perPage = (int) $request->input('rows', 20);
$page = max(1, (int) $request->input('page', 1));
$sortCol = $request->input('sidx', 'id');
$sortDir = strtolower($request->input('sord', 'asc')) === 'desc' ? 'desc' : 'asc';
$query = User::query(); // filters applied to the query, not the collection
$total = (clone $query)->count(); // count BEFORE limit/offset
$rows = $query->orderBy($sortCol, $sortDir)
->forPage($page, $perPage)
->get()
->map(fn ($u) => ['id' => $u->id, 'cell' => [$u->id, $u->name, $u->email]]);
return response()->json([
'page' => $page,
'total' => (int) ceil($total / $perPage),
'records' => $total,
'rows' => $rows,
]);
Whitelist every column name that arrives from the client. sidx and filters.rules[].field go straight into orderBy/where if you let them. Keep an explicit field → column map per grid definition and reject anything not on it — this is how you avoid SQL injection and unindexed full-table sorts at the same time.
Filtering via toolbar search. When _search=true, decode filters (it is a JSON string), then map each rules[] entry's field/op/data through the same whitelist:
if ($request->boolean('_search') && $raw = $request->input('filters')) {
foreach (json_decode($raw, true)['rules'] ?? [] as $rule) {
$column = $allowedColumns[$rule['field']] ?? null;
if (!$column) continue;
match ($rule['op']) {
'eq' => $query->where($column, $rule['data']),
'cn' => $query->where($column, 'like', '%'.$rule['data'].'%'),
// ...only the operators you actually intend to support
default => null,
};
}
}
Do not assume the bundle (or its reimplementation) sanitises operators or field names for you — treat it as your code.
Route + name indirection, carefully. The bundle-style pattern of a single generic dataAction that resolves a grid by a request-supplied name is convenient but dangerous. If you copy it, resolve only against a hardcoded allowlist (['users', 'orders']), never against an arbitrary container key.
Formatting belongs client-side wherever possible. Use jqGrid formatter/formatoptions (or your chosen table component's render hooks) for dates, badges, and action links. It keeps the PHP side serialisation-only and means the same endpoint can feed a different front-end later. Reserve server-side formatting for values that genuinely need logic the client cannot see (permissions, computed state).
Keep the front-end swappable. Because you own the envelope and the query translation, you can serve the same endpoint to jqGrid today and to TanStack Table / Livewire tomorrow. Write the controller as "produce this JSON shape from this builder," not as "produce jqGrid HTML."
require constraints conflict with PHP 8 and Laravel 10/11. --ignore-platform-reqs will download it and leave you with a runtime nothing bootstraps. Don't fight this — reimplement the contract instead.total must come from the unpaged count. Deriving it from count($rows) makes the pager collapse to a single page the moment any filter is applied. Count the cloned query first, then apply limit/offset.NeutronDataGridBundle, the repository is firano/datagrid-bundle. That usually signals a renamed fork or republished copy — verify provenance before trusting the code, and treat the MIT license as the only thing you can confirm.window.jQuery, not a module-scoped instance.OFFSET pagination degrades. jqGrid's page-number model forces offset-based paging, which is slow on large tables and blocks a keyset/cursor approach without fighting the client. Filter-heavy grids will need index review and query-plan testing regardless of how you implement it.formatter returning raw HTML strings before shipping.sidx/sord/filters verbatim, assert the resolved SQL with ->toSql() before running it, and diff your JSON envelope against the bundle's controller output field-by-field — the four envelope keys and the id/cell row shape are rigid, and a single missing key makes jqGrid render an empty grid without an obvious error.yajra/laravel-datatables + DataTables if a mature jQuery-era grid is genuinely mandated; Inertia + a modern React/Vue table for client-side richness. Each has an active maintainer and current PHP support — which this package does not.How can I help you explore Laravel packages today?