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).
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.
yajra/laravel-datatables. Treat this package as legacy-maintenance-only, and audit its transitive dependencies before running it in production.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.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().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".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.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).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./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.How can I help you explore Laravel packages today?