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

Datatables Bundle Laravel Package

lankit/datatables-bundle

A Laravel bundle for building DataTables-powered tables with server-side processing, filtering, and export support. Simplifies adding sortable, searchable, paginated data grids to PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: it is a Symfony bundle, not a Laravel package. lankit/datatables-bundle wires DataTables.js server-side processing to Doctrine ORM entities via a symfony/framework-bundle + Twig extension. There is no service provider, no facade, no config publish step. As a Laravel developer your day-to-day answer is almost always yajra/laravel-datatables; this bundle is only relevant if you inherited a Symfony 2/3 admin panel. It is also archived with 15 stars and zero dependents — treat it as read-only legacy code, not something to adopt.

If you are working in an existing Symfony app that already depends on it:

  1. composer require lankit/datatables-bundle (expect to pin PHP 7.x / Symfony 3.x; the constraints will not resolve on PHP 8 or Symfony 5+ without patching composer.json).
  2. Register the bundle in app/AppKernel.php and import its routing if the bundle ships ajax routes.
  3. Open Datatable/AbstractDatatable.php — that is the class you extend and it defines the entire contract (buildDatatable(), column builder, options, ajax config).
  4. Then read Datatable/Column/ (available column types), Datatable/Factory (the service that instantiates your table class), and Resources/views/ (the Twig blocks that emit the JS init).
  5. Skim Resources/doc and the bundle's own tests — the README is thin, and the tests are the most reliable spec for expected options.

Your first use case will be the canonical one: an admin list screen over a single entity (orders, users) where you replace hand-rolled ->setMaxResults() pagination with a sortable/searchable grid.

// src/AppBundle/Datatables/OrderDatatable.php
class OrderDatatable extends AbstractDatatable
{
    public function buildDatatable(array $options = [])
    {
        $this->ajax->set(['url' => $this->router->generate('admin_order_datatable')]);

        $this->options->set([
            'serverSide' => true,
            'processing' => true,
            'pageLength' => 25,
        ]);

        $this->columnBuilder
            ->add('id', Column::class, ['title' => 'ID'])
            ->add('customer.email', Column::class, ['title' => 'Customer', 'searchable' => true])
            ->add('total', Column::class, ['title' => 'Total', 'orderable' => true])
            ->add('createdAt', DateTimeColumn::class, ['title' => 'Created']);
    }
}

Confirm exact service ids and namespaces with bin/console debug:container | grep -i datatable before copying — this bundle predates autowiring and its ids do not follow modern conventions.

Implementation Patterns

One table class per list screen. Keep it declarative: columns and options only. Resist putting query logic in buildDatatable(); that is what makes these classes unmaintainable two years later.

Two routes per table. A page action that builds the datatable and renders Twig, and an ajax action that returns the JSON payload:

public function listAction()
{
    $datatable = $this->get('lankit.datatable.factory')->create(OrderDatatable::class);
    $datatable->buildDatatable();
    return $this->render('admin/order/list.html.twig', ['datatable' => $datatable]);
}

public function datatableAction()
{
    $datatable = $this->get('lankit.datatable.factory')->create(OrderDatatable::class);
    $datatable->buildDatatable();
    return $datatable->getResponse(); // JsonResponse
}

Server-side mode is the whole point. If serverSide is false the bundle's Doctrine layer never runs and you get a client-side table that will choke on any real table size. Set serverSide and processing explicitly; don't rely on defaults.

Association columns drive the joins. A dotted path like customer.email is resolved through Doctrine metadata, so the bundle generates the join for you. This is the package's one real convenience — and also its main performance trap (see Gotchas). Prefer mapped associations over hand-written DQL; if you genuinely need custom SQL, override the results-building method rather than fighting the column builder.

Rendering columns for links/buttons. Use an unrendered column with a Twig template for the cell body (edit/delete actions), and mark it orderable => false, searchable => false. Keep the template path inside your app bundle so it survives a bundle upgrade.

Filtering. Column-level searchable handles the global search box. For scoped filters (date ranges, status), the durable pattern is to read your filter params from the request in the ajax action and apply them to the query builder before the bundle paginates — do not attempt to encode business filters as column definitions.

Twig integration. The bundle exposes render helpers that emit both the <table> markup and the JS init block; call them inside your layout and let the bundle own the DataTables configuration so the column indices stay in sync with the server payload. Never hand-write the columns: [...] array — a mismatch here is the #1 source of "sorting does nothing."

If you are on Laravel instead, the equivalent patterns map to: a DataTable class per model (yajra's DataTable abstract class), a datatables() ajax route, ->addColumn() / ->editColumn() for rendered cells, and ->rawColumns(['actions']) for the HTML column — the shape is the same, the ergonomics are far better.

Gotchas and Tips

  • Archived means frozen. PHP 7-era syntax, getManager() on the Doctrine registry, container-as-service-locator calls ($this->get('...')), and Symfony 2/3 kernel APIs. On Symfony 4+ it will not boot without forking; on PHP 8.1+ expect deprecation noise and reflection breakage. Budget a fork, not an install.
  • Column index alignment is the silent killer. DataTables orders/sorts by zero-based index. Adding a column in Twig but not in the PHP class (or vice versa), or hiding a column with CSS instead of visible => false, shifts every subsequent sort. When sorting "does the wrong thing," verify indices before you touch the query.
  • N+1 on association columns. Traversing customer.email per row during rendering is a query per row. Check the Doctrine panel in the profiler: you should see ~1 count query + 1 select, not 1 + N. Force a fetch-join in the results query if the bundle doesn't.
  • Two queries per request. Server-side processing runs a filtered count plus the page select; both hit the same joins. On a 2M-row table with three joins this is where your 8-second response comes from. Add indexes on every sortable/searchable column before optimizing code.
  • Global search uses LIKE. On dates, booleans, enums, and numeric IDs the default search behaves absurdly. Mark those columns searchable => false and expose real filters instead.
  • XSS in rendered columns. Cell renderers return raw HTML. Escape inside your Twig template ({{ value|e }}) — don't interpolate user data into a string in PHP.
  • Debugging checklist when the grid shows raw JSON or spins forever: (1) serverSide true? (2) ajax URL correct and returning 200, not a login redirect? (3) response shape has draw, recordsTotal, recordsFiltered, data? (4) column count matches? Check the network tab first, then the bundle's getResponse().
  • Extension points worth knowing: custom column classes (resolved by FQCN, so no config registration needed), overriding AbstractDatatable hooks, and a post-build listener if you need cross-cutting behavior. All of these are unmaintained APIs — pin your Composer lock and vendor-fork rather than relying on upstream fixes.
  • Have an exit plan. With 0 dependents and archived status, you are the only maintainer of this code path. Isolate table classes behind your own thin interface so swapping to SgDatatablesBundle, a hand-rolled API endpoint, or (on Laravel) yajra/laravel-datatables is a directory change rather than a rewrite of every admin controller.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource