Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Datatablesbundle Laravel Package

sst/datatablesbundle

SST DataTablesBundle integrates jQuery DataTables with Symfony apps, enabling server-side AJAX grids backed by Doctrine. Build sortable, filterable, paginated tables with customizable columns, minimal configuration, and support for admin dashboards and data-heavy CRUD screens.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Confirm what you actually installed. The repo folder (SSt/DataTablesBundle) and the Composer name (sst/datatablesbundle) don't match — before wiring anything up, open the package's composer.json and read name, type (symfony-bundle), require (Symfony/PHP/Doctrine versions), and autoload.psr-4 for the real namespace (likely SSt\DataTablesBundle\... or an inherited Ali\... namespace). Then read DependencyInjection/Configuration.php — it tells you the exact config root key and every knob. Also note the version you land on: 1.8.1 is the current patch and only silences PHP deprecation warnings (null used as an array key, self-referencing string offsets) — no API or behaviour change, so anything written against 1.8.0 is still valid. Pin ~1.8.1 rather than dev-master; the 1.8.x line is where the PHP 8.4/8.5 notice noise is actually cleared.

2. Register the bundle.

// Symfony 2/3: app/AppKernel.php
new SSt\DataTablesBundle\SStDataTablesBundle(),

// Symfony 4+/5: config/bundles.php
SSt\DataTablesBundle\SStDataTablesBundle::class => ['all' => true],

3. Import the bundled AJAX route. The table's data endpoint is a route shipped by the bundle; without it the grid renders and then spins forever.

# config/routes.yaml
datatables:
    resource: "@SStDataTablesBundle/Resources/config/routing.yml"
    prefix: /datatable

4. First use case — replace a hand-rolled findAll() + pagination controller with a read-only grid over one Doctrine entity:

public function indexAction()
{
    $dt = $this->get('datatable')                 // or inject via constructor
        ->setName('orders')
        ->setEntity(Order::class, 'o')            // alias is mandatory
        ->setFields([
            '#'        => 'o.id',
            'Customer' => 'o.customerName',
            'Total'    => 'o.total',
            'Placed'   => 'o.createdAt',
        ])
        ->setRenderList(['o.id', 'o.customerName', 'o.total', 'o.createdAt'])
        ->setOrder('o.id', 'desc')
        ->setHasAction(true);                     // adds the action column

    return ['datatable' => $dt];
}
{# templates/order/index.html.twig #}
{{ datatable_render(datatable) }}

Where to look first, in this order: Resources/config/services.yml (service id for the factory — everything hangs off it), Util/Factory/ (the fluent builder you call from controllers), Twig/ (the datatable_render* functions), Controller/ (the AJAX endpoint you'll need to secure/guard), Resources/views/ (the templates you'll override).

Implementation Patterns

Keep builders in a service, not the controller. The fluent chain is stateful and returns $this; a small DatatableBuilder service per screen makes the config testable and lets you reuse the same grid behind two routes.

Joins and related entities. Register joins before you reference them in setFields, then use the join alias in both fields and render list. Aggregates need groupBy alongside them, and every non-aggregated selected column must be in the group-by or the query fails at the DB, not at the bundle.

$dt->setEntity(Order::class, 'o')
   ->addJoin('o.customer', 'c', 'left')        // check the exact method name in the factory
   ->setFields(['Customer' => 'c.name', 'Lines' => 'COUNT(l.id)'])
   ->setRenderList(['c.name', 'COUNT(l.id)'])
   ->setGroupBy('o.id');

Tenant / permission filtering. The bundled AJAX controller builds the query from the request, so anything user-specific must be baked into the datatable definition — resolve it in a request-scoped builder (setWhere('o.tenant = :t')->setParameter('t', $tenant->getId())) rather than trusting the URL.

Actions column. setHasAction(true) only reserves the column; you supply the buttons in the renderer template. Override the template per-datatable (setRenderer(...) / theme config) rather than editing vendor files, so Composer updates don't eat your changes.

Multiple grids on one page. Give each one an explicit setName(); the generated JS and the AJAX round-trip key off it. Two unnamed datatables in one template will fight over the DOM ids.

Export / "all rows" path. There's no CSV export. The cheap pattern is a second route that reuses the same builder, forces setMaxResults(null)-style paging off, calls the query builder directly, and streams a CSV — never try to get "all rows" out of the AJAX endpoint by passing a huge page size.

Progressive enhancement of the theme. Swap the renderer/theme (Bootstrap vs. jQuery UI in the AliDatatable lineage) via config, and keep your own CSS in a separate file layered on top; the shipped templates carry inline styling you'll otherwise be fighting.

Gotchas and Tips

  • _identifier_ is load-bearing. The _identifier_ field key marks the primary key used for row ids and action links. If it's missing or not also present in the render list, actions break silently (rows render, buttons do nothing).
  • setFields keys vs. setRenderList values are different things. Keys are column headers; render-list entries must be the DQL values (o.createdAt), not the header labels. Mismatches produce empty columns with no exception.
  • Every field value must be a real DQL expression. 'o.status' is fine; 'status' or a raw SQL function will blow up at query time, and the error surfaces as a failed AJAX call — check the browser network tab and var/log/dev.log, not the page.
  • DataTables version mismatch is the #1 "it just doesn't load" cause. This bundle speaks the legacy 1.9 request/response protocol (iDisplayStart, sSearch, iSortCol_0, aaData). A modern DataTables 1.10+ ajax config sends start/length/search[value] and gets an empty table back. Pin the jQuery DataTables version the bundle's assets expect, or add a client-side adapter that rewrites the params.
  • Search over non-string columns breaks. A global LIKE across a datetime, boolean, or joined entity column throws or returns nothing. Restrict the searchable set explicitly, and format dates/booleans in the renderer, not in the query.
  • The pagination total is the usual suspect when counts lie. The bundle issues a separate COUNT derived from your query; with GROUP BY/HAVING that count is frequently wrong ("showing 1–10 of 3"). Verify against the profiler's Doctrine panel and fall back to a hand-written count if needed.
  • The AJAX route is public by default. Put it behind security.yaml (IS_AUTHENTICATED_FULLY) or define your own route wrapping the controller. It's a GET, so no CSRF protection — never expose mutations through it.
  • Debugging recipe: enable the Symfony profiler, watch the Doctrine panel for the actual generated SQL, and dump() the request query params in a kernel.request listener if the grid returns 0 rows. Sort/search bugs are almost always a field alias typo, visible only in the SQL.
  • Upgrade to 1.8.1 for log hygiene, not behaviour. The patch removes two PHP deprecation notices (null as an array key in the request-param handling and self-referencing string offset manipulation), which otherwise spray var/log/dev.log and can be promoted to fatal by a strict ErrorHandler or by zend.exception_ignore_args-style test configs. Do it as a plain composer update sst/datatablesbundle; there is nothing to change in your code, and no config keys moved between 1.8.0 and 1.8.1. If you're still on 1.7.x, treat it as a normal minor bump and re-run your grid smoke tests.
  • Maintenance risk is real, though 1.8.1 is a positive signal. Zero stars, zero dependents, Symfony 2/3-era idioms ($this->get('datatable') string service lookup, bundle-registered routes). A patch release that chases PHP deprecations means someone is still running it against a modern PHP, but the fix is cosmetic — don't read it as a roadmap. It still runs fine on 4.4/5.4 with autowiring aliases, but budget for: aliasing the service in services.yaml, replacing $this->get() with constructor injection, and eventually porting to a maintained alternative. Keep your datatable definitions isolated in builder services so that swap is a one-file change.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle