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

Datatable Bundle Laravel Package

sigmasoft/datatable-bundle

Sigmasoft Datatable Bundle adds configurable server-side data tables to PHP applications, with sorting, filtering, pagination, and search support. Define columns and data sources in a few lines to render reusable, responsive tables.

View on GitHub
Deep Wiki
Context7

Getting Started

Before installing anything, confirm what the package actually is. The name says Bundle (Symfony terminology) but the description claims a Laravel package. Check composer.json on the GitHub repo for laravel/framework / illuminate/* constraints and PHP version first — if it only declares Symfony/Doctrine classes, stop, because you are looking at the wrong framework. The rest of this guide assumes the Laravel claim holds.

Minimal install:

composer require sigmasoft/datatable-bundle

Laravel auto-discovery should register the service provider. If not (zero stars means the metadata may be incomplete), add it to config/app.php providers and try to publish config:

php artisan vendor:publish --tag=datatable-config

If no tag exists, there is nothing to publish — configure via the fluent API only.

Where to look first. There is no meaningful documentation beyond the README, so read src/ in this order:

  1. The service provider — reveals auto-discovery, config, and Blade directives/extensions.
  2. The base DataTable class (or whatever the fluent builder is called) — this is the API.
  3. Any column/formatter abstraction — this tells you what rendering hooks exist.
  4. The JSON response shaper — confirms whether it speaks DataTables.net (draw/recordsTotal/recordsFiltered/data) or a custom envelope.

Your first use case. A read-only users list view for an internal admin page: server-side pagination, sortable id/email/created_at, and a global search over name/email. This is exactly the shape the package advertises and the simplest thing to validate against.

Route + controller:

// routes/web.php
Route::get('/admin/users', [UserController::class, 'index']);
Route::get('/admin/users/datatable', [UserController::class, 'datatable'])->name('users.datatable');

// app/Http/Controllers/UserController.php
public function index()
{
    return view('admin.users.index');
}

public function datatable(UserDataTable $table)
{
    return $table->query(User::query())->toJson();
}

Blade: include the client-side assets yourself (jQuery + DataTables.net). The package ships the server side, not the JS/CSS. Point the DataTables ajax URL at route('users.datatable') and declare columns to match the keys your DataTable returns.

If the fluent API is DataTable::make(User::query())->column('id')->column('email', 'Email')..., wire your first table that way and verify the JSON shape in the browser network tab before moving on.

Implementation Patterns

One DataTable class per resource. Resist the temptation to inline DataTable::make(...) in every controller — you will repeat column config across list views, and the class becomes the single place to add a filter or change a formatter. Follow the Laravel convention of a dedicated class per list:

class UserDataTable extends DataTable
{
    public function query()
    {
        return User::query()->with('roles')  // eager load to kill N+1
            ->select(['users.id', 'users.name', 'users.email', 'users.created_at']);
    }

    public function columns(): array
    {
        return [
            Column::make('id')->sortable()->searchable(),
            Column::make('name')->sortable()->searchable(),
            Column::make('email')->sortable()->searchable(),
            Column::make('roles.name', 'Roles')->formatter(fn ($v) => implode(', ', (array) $v)),
            Column::make('created_at')->sortable()->formatter(fn ($v) => Carbon::parse($v)->diffForHumans()),
        ];
    }
}

Keep formatters server-side only where possible. Client-side rendering via JS render: callbacks is fine for display, but if the formatter output is also used in CSV export, doing it in PHP (the package's fluent formatter hook) avoids drift between the table and the export.

Controller endpoint pattern. Always split the HTML page from the JSON endpoint. The page route returns the Blade shell; a separate GET returns JSON. This keeps ajax retries cheap and makes authorization per-endpoint explicit.

Authorization belongs on the query, not on the DataTable. The package will happily return whatever rows your base query returns. Apply tenant scoping and policy checks in query():

public function query()
{
    $this->authorize('viewAny', User::class);
    return User::query()->where('tenant_id', auth()->user()->tenant_id);
}

For per-column visibility, filter the columns() array by ability — some builders expose can() on Column, some don't. If not, hide it at the DataTable class level.

Filters as explicit methods, not pass-through. If the package supports custom filters, register them with named, whitelisted callbacks:

$this->filter('status', fn ($q, $value) => $q->where('status', $value));

Only expose a fixed set of filter keys. Never forward request('filter') wholesale into the query builder — that is the SQL injection / tenant-leak footgun.

Global search. If the package supports searchable() on columns, use it. If not, wire the search term into query() via a single where clause over a fixed column list. Never interpolate the search term into SQL.

Workflow for a new admin screen:

  1. Write the DataTable class with columns first; run the JSON endpoint and eyeball the response.
  2. Wire the Blade view + DataTables init.
  3. Add sortable/searchable flags on the columns you want the client to control.
  4. Add server-side filters for the sidebar dropdowns.
  5. Add formatters for display-only columns.
  6. Add the "show/hide columns" UI on the client only if the package whitelists by column key — otherwise it becomes an injection surface.
  7. If you need a CSV export, use the package's exporter hook if it exists; otherwise write a streaming export controller that reuses the same query() method.

Integration tip: feature-flag it. Since the package (by design) touches every list view, and it is unproven, gate it behind a config flag (config('datatable.engine')) so you can flip back to hand-rolled or Yajra without a code change on every controller. Wrap the fluent builder in your own thin AdminList helper — every controller depends on your helper, not on sigmasoft/* classes directly. This makes the "replace the package" path a one-file change.

Gotchas and Tips

Verify it's actually Laravel before you commit. The name datatable-bundle strongly suggests a Symfony bundle (Doctrine-based, Twig-based). The description says Laravel; the two assessments in the source materials disagree. Check the namespace (Sigmasoft\... vs Illuminate\...), composer.json, and whether the service provider extends Illuminate\Support\ServiceProvider. If it's Symfony, this package cannot be used in a Laravel app without a bridge, and the time cost of building one dwarfs any benefit over Yajra.

Zero stars, zero dependents means no Stack Overflow corpus. When something breaks, you will read source, not documentation. Budget half a day to reach a first working table, but one to two days to reach confidence on authorization and query shape — that second half is the part no README replaces.

Pin to a commit, not a version range. With no visible release cadence, ^1.0 may point at a stale branch. In composer.json:

"sigmasoft/datatable-bundle": "dev-main#abc1234"

This is the cheapest form of supply-chain discipline available for an unaudited package.

Watch the total-count query. Most datatable implementations run a SELECT count(*) for recordsTotal and another for recordsFiltered. On a wide, unfiltered table with millions of rows this dominates latency. Log both queries in local dev and check timing:

DB::listen(fn ($q) => logger()->debug($q->sql, $q->bindings));

If counts are slow, either constrain the table to an indexed filter or add a ->simplePaginate()-style mode if the package supports it. Assume no cursor pagination unless the source says otherwise.

Eager-load everything the formatter touches. A formatter accessing $row->roles->pluck('name') will silently N+1 the moment a list has more than a handful of rows. ->with() in query() is not optional; it is the difference between a 40ms and a 4s page.

Column names are the attack surface. Packages in this space frequently accept column identifiers/sort keys from the client and pass them straight into orderBy/select. Verify whitelisting in the source before any non-internal use. If the whitelist is derived from your columns() config, you are probably fine; if it trusts request('columns'), it is not. Test with a crafted request:

GET /admin/users/datatable?columns[0][data]=id,(select 1)

Don't use it for customer-facing or high-traffic surfaces. This is a developer-productivity tool for internal list views, not a performance layer. Anything with SLA implications goes behind either an internal abstraction you already own, or a mature alternative.

Debugging tips:

  • Enable Laravel Debugbar and inspect the two count queries plus the data query for every request.
  • Log $request->all() inside the datatable endpoint in a local environment to see exactly what the client is sending — you will usually find that the "extra" parameters are columns you haven't whitelisted.
  • If the JSON envelope doesn't match what DataTables.net expects (draw, data, recordsTotal, recordsFiltered), the table renders as "Loading…" forever. Compare the response with the DataTables.net server-side protocol doc and adapt in a subclass rather than patching the vendor directory.

Extension points to look for in the source, in priority order:

  1. Column whitelist / columns() — the safe path.
  2. Custom filter registration — named callbacks, not closures on request input.
  3. Formatter hook — for display and export parity.
  4. Export / serializer hook — for CSV/XLSX, if present.
  5. Action buttons / row-action registration — for "Edit", "Delete", "View" buttons in each row. If missing, render them client-side from the row's id.

If only one or two of those exist, the package's day-to-day value is a per-table JSON endpoint helper — nothing more. Treat it that way: adopt it as a helper, not as the data layer. And keep a hand-rolled fallback in your notes:

// Fallback: 30 lines of query builder + a Blade component
$query = User::query()->select([...]);
if ($search = request('search')) { $query->where(fn($q) => $q->where('name','like',"%$search%")->orWhere(...)); }
$rows = $query->orderBy(request('sort','id'), request('dir','asc'))->paginate(request('per_page', 25));
return response()->json(['data' => $rows->items(), 'recordsTotal' => $rows->total(), ...]);

If that fallback is genuinely comparable, choose it — it eliminates the dependency risk entirely for the same amount of code.

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