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

openskill/datatable

OpenSkill Datatable is a Laravel package for building server-side data tables from Eloquent or query builder sources, with column formatting, search, sorting, filtering, pagination, and AJAX-ready responses for flexible admin interfaces.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Confirm your stack first. This package targets Laravel 5.x and PHP 5.5/5.6-era APIs. It was last released in January 2016 and has no commits since, so before installing anything, check composer.json in the repo against your PHP/Laravel version. On Laravel 6+ or PHP 8 you will hit fatal errors (helper removals, facade/contract signature changes, Illuminate\Support\Facades\Input removal).
  • Install: composer require openskill/datatable. On Laravel 5.0–5.4 you must manually register the service provider in config/app.php and add the facade alias; package auto-discovery did not exist in that era.
  • Publish the config: php artisan vendor:publish --provider="Openskill\Datatable\DatatableServiceProvider" (or the --tag=config variant shown in the README). This gives you config/datatable.php, which is where the client-plugin driver, default query-string keys, and column defaults live.
  • Look here first, in this order: the repo README.md, then src/ for the driver interface and the bundled driver(s) (jQuery DataTables, Bootstrap Table, etc.), then config/datatable.php, then tests/ for canonical request/response examples. The tests are usually the most accurate documentation for an abandoned package.
  • First use case — wire up one table end to end. Add a named route that returns JSON, point a jQuery DataTables ajax URL at it, and verify the handshake before adding any complexity:
// routes/web.php
Route::get('users/datatable', 'UserDatatableController@json')->name('users.datatable');
// app/Http/Controllers/UserDatatableController.php
public function json()
{
    return Datatable::make(User::query())
        ->columns([
            'id',
            'name',
            'email',
            'created_at',
        ])
        ->make();
}
$('#users').DataTable({
    processing: true,
    serverSide: true,
    ajax: '{{ route('users.datatable') }}',
    columns: [
        { data: 'id' },
        { data: 'name' },
        { data: 'email' },
        { data: 'created_at' }
    ]
});

If the table draws rows but shows "No data", the request/response contract is wrong — not your query. Get this minimal case working before you touch joins, formatters, or relations.

Implementation Patterns

  • One datatable definition per resource. Keep controllers thin. Either a dedicated invokable controller per table, or a small definition class (app/DataTables/UserDatatable.php) that encapsulates the base query, column list, searchable/orderable whitelist, and formatters. The package is query-oriented; the value you add is the structure around it.
  • Always start from an Eloquent/Query builder, not a collection. The whole point of server-side processing is pushing LIMIT/OFFSET, WHERE ... LIKE, and ORDER BY to the database. ->get() before handing off defeats the package and will blow up on large tables.
  • Lock down the query before the package sees it. Authorization and tenancy filters belong on the builder you pass in, not in client-side column config:
$query = User::query()
    ->where('tenant_id', auth()->user()->tenant_id)
    ->with('role'); // eager-load anything rendered in a column

return Datatable::make($query)
    ->columns(['id', 'name', 'email', 'role.name', 'created_at'])
    ->make();
  • Relations: eager-load for display, join for sorting. Accessor/relation columns render fine via with(), but ordering or searching on role.name usually requires an explicit join plus a qualified column name in the column map. Decide per column whether it needs to be sortable, and map it explicitly if so.
  • Use formatters for display, raw values for sort. Action buttons, badges, date formatting, and links should be produced by a column formatter/closure. Keep the underlying value the raw DB value so ordering stays correct — never sort on rendered HTML.
  • Whitelist everything the client can influence. Searchable columns, orderable columns, and page length should all be explicit. Any package of this vintage that reads order[0][column] and columns[i][data] from the request is one bad mapping away from an ambiguous-column SQL error or an injection into ORDER BY.
  • Drive it by config, not by hardcoding. Set the client-plugin driver (datatables, bootstrap-table, etc.) in config/datatable.php so the request/response key mapping matches the JS plugin you actually loaded. If you support two plugins, use two config profiles or pass the driver per call rather than mutating global config at runtime.
  • Blade + JS integration: keep the AJAX URL as a named route so URL changes don't break the table; keep column definitions in one JS file per table; disable client-side ordering/search where the server handles it to avoid double-filtering confusion.
  • Test the JSON, not the browser. Write a feature test that asserts the response shape (draw, recordsTotal, recordsFiltered, data) and that a search param narrows recordsFiltered. This catches regressions when you add joins far more cheaply than clicking around.

Gotchas and Tips

  • This is effectively unmaintained. No release since 2016-01-18 and 52 stars means very few people have hit your exact edge case. Budget for reading the source rather than the docs, and seriously consider yajra/laravel-datatables for new work — it is actively maintained and supports modern Laravel.
  • Version pinning is not optional. Pin the exact version in composer.json. A composer update on a Laravel 5 project can pull a transitive dependency that breaks this package silently.
  • The #1 failure mode is a column-name mismatch. The client columns[].data values must line up with what the server returns. Symptom: request succeeds, table shows "No data" or blank cells. Open the browser Network tab, inspect the JSON payload, and compare keys one by one. Also check that draw is echoed back — a missing draw makes DataTables discard the response.
  • Ambiguous column errors on joins. When you join roles, id, name, and created_at become ambiguous. Qualify them (users.id, users.name) in the column map, or the generated ORDER BY/WHERE will throw. This is the second most common failure after mismatched column keys.
  • Cached config will bite you. After editing config/datatable.php (especially the driver), run php artisan config:clear. On a cached-config production deploy, a driver change that works locally will not take effect.
  • Request mutation surprises. The package reads the raw query string (start, length, search[value], order[...]). If middleware or a FormRequest normalizes the request, those keys can disappear. Test with the actual AJAX request, not a manually constructed one.
  • CSRF only matters if you POST. Most setups use GET, but if you switch the AJAX method to POST you must add the token header or the request silently 419s.
  • XSS via formatters. Because the package will happily return whatever your formatter produces, any user-controlled text rendered into a formatted column must be escaped (e() / htmlspecialchars) unless you deliberately trust it.
  • Debug the SQL, not the package. When a filter returns nothing, dump the query with ->toSql() and ->getBindings() before handing it to the package, and use DB::listen() to see what actually executed. Debugging at the SQL layer is faster than stepping through an unfamiliar 2016 codebase.
  • Count queries can dominate. Server-side tables typically run a separate COUNT(*); on large joins this is slower than the data query. Select only the columns you render and index the columns you mark searchable/orderable.
  • Extension points to reach for: a custom driver implementing the package's driver contract if you need a plugin it doesn't ship, column formatter callbacks for rendering, and config overrides for request/response key names if your JS plugin uses non-standard parameters. Read src/ for the interface before forking — forking is often cleaner than fighting an abandoned 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.
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