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 Laravel Package

chumper/datatable

View on GitHub
Deep Wiki
Context7

Getting Started

Install into a Laravel 4 app (this is the hard constraint — the package targets illuminate/support ~4.0 and will not resolve on Laravel 5+):

"require": {
    "chumper/datatable": "dev-master"
}

Register the service provider and facade in app/config/app.php:

'providers' => [
    ...
    'Chumper\Datatable\DatatableServiceProvider',
],
'aliases' => [
    ...
    'Datatable' => 'Chumper\Datatable\Facades\Datatable',
],

Publish the config if you need to change the JS asset path or default table classes:

php artisan config:publish chumper/datatable

The first thing to read is Chumper\Datatable\Datatable — it exposes the four entry points you will use every day: collection(), query(), eloquent(), and table(). The first three are server-side (they return JSON for DataTables to consume); table() is client-side (it renders an empty <table> plus JS init).

First use case — a paginated users grid with an edit link:

// routes.php
Route::get('users', 'UserController@index');
Route::get('users/data', 'UserController@data');

// UserController
public function index()
{
    return View::make('users.index');
}

public function data()
{
    return Datatable::query(User::select('id', 'name', 'email'))
        ->showColumns('id', 'name', 'email')
        ->searchColumns('name', 'email')
        ->orderColumns('id', 'name', 'email')
        ->addColumn('action', function ($row) {
            return '<a href="'.URL::route('users.edit', $row->id).'">Edit</a>';
        })
        ->make();
}
{{-- users/index.blade.php --}}
<table id="users" class="table table-striped">
  <thead>
    <tr><th>ID</th><th>Name</th><th>Email</th><th>Actions</th></tr>
  </thead>
</table>

<script>
$(function () {
    $('#users').dataTable({
        "bProcessing": true,
        "bServerSide": true,
        "sAjaxSource": "{{ URL::route('users.data') }}"
    });
});
</script>

That round-trip is the whole package in miniature: query() wraps a query builder, showColumns() maps DB columns to DataTables aoColumns, and make() returns a JsonResponse with the 1.9-style envelope (sEcho, iTotalRecords, iTotalDisplayRecords, aaData).

Implementation Patterns

Prefer query() over collection(). collection() hydrates the entire result set in PHP and paginates in memory — fine for a 200-row lookup table, dangerous for anything transactional. query() pushes paging, search, and ordering into SQL:

return Datatable::query(
        DB::table('orders')
            ->join('customers', 'customers.id', '=', 'orders.customer_id')
            ->select('orders.id', 'customers.name as customer', 'orders.total', 'orders.created_at')
    )
    ->showColumns('id', 'customer', 'total', 'created_at')
    ->searchColumns('customers.name', 'orders.total')
    ->orderColumns('orders.id', 'orders.total', 'orders.created_at')
    ->make();

Eloquent with relationships. Because collection() gives you model instances, it's the right tool when an action column needs model logic. Eager-load to avoid the N+1 that a lazy $row->customer->name inside the render callback would otherwise create:

$users = User::with('company')->get();

return Datatable::collection($users)
    ->showColumns('id', 'name')
    ->addColumn('company', function ($user) {
        return $user->company ? $user->company->name : '—';
    })
    ->make();

Action / computed columns. addColumn() registers a virtual column appended after the real ones (or interleaved if you pass an index). The callback receives the raw row — stdClass for query(), a model for collection():

->addColumn('actions', function ($row) use ($canEdit) {
    $html = '<a href="'.URL::route('orders.show', $row->id).'">View</a>';
    if ($canEdit) {
        $html .= ' | <a href="'.URL::route('orders.edit', $row->id).'">Edit</a>';
    }
    return $html;
})

Aliasing raw SQL. If you aggregate or join, alias columns in SQL and match the alias in showColumns(); use setAlias() when the DB column name and the DataTables index diverge:

Datatable::query(
        DB::table('orders')
          ->select('orders.id', DB::raw('SUM(order_lines.qty) as units'))
          ->groupBy('orders.id')
    )
    ->showColumns('id', 'units')
    ->setAlias('units', 'total_units')
    ->make();

Client-side rendering. When you want DataTables to handle everything in the browser (small, static datasets), use table():

public function index()
{
    $table = Datatable::table()
        ->addColumn('ID', 'Name', 'Email')
        ->setUrl(URL::route('users.data'))
        ->setOptions(['bFilter' => false, 'iDisplayLength' => 25])
        ->render();

    return View::make('users.index', compact('table'));
}
{{ $table }}
{{-- plus the package's script block / init helper --}}

Search vs. order allowlists. searchColumns() and orderColumns() are not inferred — they are the whitelist of what the client is allowed to sort/filter on. Wire them explicitly, especially for joined queries, or DataTables will silently fail to sort (or, worse, let a user sort by an unindexed column across a big join).

Composition with existing scopes. query() accepts anything that behaves like an Illuminate\Database\Query\Builder, so keep authorization and soft-deletes in your model/scopes and only hand the final builder to the package:

return Datatable::query(
        Order::where('tenant_id', Auth::user()->tenant_id)->getQuery()
    )
    ->showColumns('id', 'status', 'total')
    ->make();

Multiple tables on one page. Each query()/collection() chain is stateless — just route each table to its own getXxxData action and give each <table> a distinct id and sAjaxSource.

Gotchas and Tips

  • It's archived and Laravel 4 only. No PHP 7.4+/Laravel 8+ support, no security patches. If you're starting a new project, use yajra/laravel-datatables. Treat this package as legacy-maintenance-only, and audit its transitive dependencies before running it in production.
  • DataTables 1.9 protocol. The server response uses sEcho, iTotalRecords, iTotalDisplayRecords, aaData, and the request parsing expects iDisplayStart, iDisplayLength, sSearch, iSortCol_0, sSortDir_0. If you drop in DataTables 1.10+ without the legacy shim, sorting and paging silently break — pin the 1.9 JS or add the compat shim.
  • Column count must match. showColumns() + addColumn() produce the aoColumns count; your Blade <thead> must have the same number of <th>s or sorting indexes shift by one. Easily the most common bug report.
  • collection() is a memory trap. Paging and search happen in PHP after ->get(). A User::all() on 100k rows will OOM before DataTables ever sees it. Use query().
  • No escaping on addColumn(). The callback output is emitted raw — escape $row->name with e() yourself, or you've built an XSS vector.
  • searchColumns() uses DB column names, not DataTables display names. With joins you need table-qualified names (customers.name), not the alias used in showColumns().
  • make() sets JSON headers. If a global after filter or a debugging middleware appends to the response body, DataTables gets malformed JSON. Check the Network tab's raw payload first when "it just spins".
  • CSRF and verb. sAjaxSource issues a GET; don't point it at a POST route. If your app enforces CSRF on GET-less setups (rare), add the token to the DataTables ajax config.
  • Sorting a computed column. Columns added via addColumn() aren't in orderColumns() and can't be server-sorted without a matching SQL expression — either sort in SQL by aliasing, or disable sort on that column client-side ("bSortable": false).
  • Dates and Carbon. showColumns('created_at') emits the raw DB string. Format inside addColumn() or via a model accessor; DataTables' client-side date sorting won't understand Y-m-d H:i:s reliably.
  • Extension points are thin. There's no event system or column-renderer registry. For reusable behavior (currency, badges, status pills), wrap the chain in your own base controller method rather than subclassing the package — the internals aren't designed for extension and the version pin makes upstream patching moot.
  • Debugging tip. Hit the data route directly in the browser (/users/data?sEcho=1&iDisplayStart=0&iDisplayLength=10) and read the JSON. If aaData looks right but the grid doesn't render, the mismatch is in aoColumns/sAjaxSource, not in the package.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core