evolutto/ajaxgrid
TaoSistemasAjaxGridBundle (evolutto/ajaxgrid) is an AJAX grid bundle for Symfony 3 that adds AJAX-driven data grid functionality to Symfony applications. The README is minimal, so inspect the source and confirm Symfony 3 compatibility before adopting it.
First, the honest framing: this is a Symfony 3 bundle, not a Laravel package. If you're a Laravel developer with no Symfony 3 codebase in front of you, you will not install this — you'll read it for pattern ideas and then reach for Livewire, Inertia, Filament tables, or plain DataTables + a JSON endpoint. Keep this assessment in the "legacy maintenance" column.
If you are inheriting a Symfony 3.2/3.4 app that already depends on it:
Read the source before the README. The README is a title and a one-line description — there is no documented API. Your real onboarding is the repo tree. Spend your first 20 minutes on:
composer.json — confirms the package name (evolutto/ajaxgrid), the PHP constraint, and which Symfony components it pins. This tells you whether it will even install alongside your app's other deps.DependencyInjection/ — the extension class and its alias. The alias is what your config keys are named after, so you need it before you can configure anything.Controller/ — the action that serves the grid rows. This is the contract: request parameter names, response shape, paging/sorting/filtering conventions.Resources/public/ — the JavaScript. Whatever the controller returns has to satisfy this file, so read both sides together.Resources/views/ — the Twig template names you'll render or override.Install and register. Symfony 3 registration is manual, in app/AppKernel.php:
// VERIFY the class name against composer.json's autoload/psr-4 block
new TaoSistemas\AjaxGridBundle\TaoSistemasAjaxGridBundle(),
Then install the bundle's assets: bin/console assets:install --symlink.
Set up routing. Either import the bundle's routing file if it ships one, or define your own route pointing at your own controller that reuses the bundle's grid service. Importing the bundle's route is the fastest path to a working screen.
First use case: a single index page listing one Doctrine entity with server-side paging. Render the bundle's template (or your own page that includes its JS), point the grid at the JSON endpoint, and confirm you get { rows, total }-ish back. Once paging works, add sorting, then a filter. Doing it in that order isolates the inevitable parameter-name mismatches.
Server-side endpoint (the core pattern). An AJAX grid is always the same triangle: a paged/sorted/filtered query on the server, a JSON shape on the wire, and a widget that re-requests on every interaction. Expect this bundle to expect something close to:
GET /grid/orders?page=1&limit=25&sort=createdAt&order=desc&filter[status]=paid
→ { "rows": [...], "total": 1483 }
Do not assume the parameter names — check the controller. Classic-era grids often use page/rows/sidx/sord rather than page/limit/sort/order. Getting these wrong produces the maddening symptom of "the grid renders but paging always shows page 1."
Subclass rather than fork. The usual extension point in bundles of this vintage is a base grid class or a configurable service. The pattern that ages well:
// Illustrative — confirm the actual base class and method names in vendor/
class OrdersGrid extends BaseGrid
{
protected function buildQuery() // or getQueryBuilder(), getData(), etc.
{
return $this->em->createQueryBuilder()
->select('o')
->from(Order::class, 'o');
}
}
Register it as a service in your app's services.yml, then either inject it into your controller or override the bundle's service definition via a compiler pass / service alias. Overriding a bundle service by redefining the same ID in your app config is the Symfony 3 way and avoids patching vendor code.
Keep the query builder honest. The classic failure mode of these bundles is fetching entities and hydrating everything, then slicing in PHP. If the bundle does that, your grid will be fine at 500 rows and unusable at 50,000. Push pagination into Doctrine: use Paginator ($paginator->paginate($qb, $page, $limit)) and count separately, and join eagerly on any column you display so you don't emit one query per row.
Whitelist sort columns. Any grid that accepts a sort= parameter straight from the query string is a SQL-injection surface if it's interpolated into orderBy(). Map incoming sort keys to real field names through an array before touching the query builder.
Template override, not template edit. Symfony resolves app/Resources/TaoSistemasAjaxGridBundle/views/foo.html.twig ahead of the bundle's own copy. That's your customization path and it survives composer update.
If you're porting the idea to Laravel (the likelier scenario for you): the whole thing decomposes into a controller method returning ->paginate($request->per_page), a Blade partial, and a fetch/Axios call on interaction. Livewire or Inertia gets you there with less code than resurrecting a Symfony 3 bundle. The only thing worth stealing is the request/response contract.
Resources/public is not web-accessible until assets:install. Use --symlink in dev so JS edits are live. If the grid renders unstyled with a 404 on a .js file, this is why._format. Many of these endpoints key off Request::isXmlHttpRequest() or a _format: json route requirement. If you hit the URL in a browser tab and it 500s or returns the HTML page, that's often the cause — test with the XHR header or the correct extension.vendor/evolutto/ajaxgrid directly. Subclass the grid, override the service, or override the template in app/Resources/. If you must patch, fork the repo and point your composer.json at the fork with a VCS repository entry — and record why.DependencyInjection/Configuration.php (tells you what's configurable), tagged services, and any interface the bundle type-hints. Those three are the map.How can I help you explore Laravel packages today?