Installation
composer require omines/datatables-bundle
Enable the bundle in config/bundles.php:
return [
// ...
Omines\DataTablesBundle\OminesDataTablesBundle::class => ['all' => true],
];
Basic Controller Integration Create a controller with a DataTables-compatible response:
use Omines\DataTablesBundle\DataTables;
use Omines\DataTablesBundle\DataTables\DataTable;
use Omines\DataTablesBundle\DataTables\LengthAwarePaginatorAdapter;
#[Route('/users', name: 'users_datatable')]
public function usersDataTable(DataTables $datatables): JsonResponse
{
$users = UserRepository::all(); // Your Eloquent/Doctrine query
$adapter = new LengthAwarePaginatorAdapter($users);
$table = $datatables->of($adapter);
return $table->make(true)->json();
}
Frontend Setup Include DataTables CSS/JS in your Twig template:
{{ encore_entry_link_tags('app') }}
<link rel="stylesheet" href="https://cdn.datatables.net/1.11.5/css/jquery.dataTables.min.css">
<script src="https://cdn.datatables.net/1.11.5/js/jquery.dataTables.min.js"></script>
Initialize the table:
$(document).ready(function() {
$('#users-table').DataTable({
processing: true,
serverSide: true,
ajax: '/users'
});
});
Use makeColumn() to define columns with custom logic:
$table = $datatables->of($adapter)
->addColumn('full_name', function($user) {
return $user->first_name . ' ' . $user->last_name;
})
->editColumn('active', function($user) {
return $user->active ? '<span class="badge badge-success">Active</span>' : '<span class="badge badge-danger">Inactive</span>';
})
->make(true);
Leverage built-in search/filtering:
$table = $datatables->of($adapter)
->add('name', function($query) {
$search = $this->getParameter('search.value');
return $query->where('name', 'like', "%{$search}%");
})
->make(true);
For large datasets, use server-side processing (default):
// Controller
$table = $datatables->of($adapter)
->setServerSide(true)
->make(true);
// Frontend (already configured in JS snippet above)
Enable CSV/Excel exports:
$table = $datatables->of($adapter)
->add('export', function($query) {
return $query->select('id', 'name', 'email');
})
->make(true);
Frontend:
$('#users-table').DataTable({
// ... existing config
dom: 'Bfrtip',
buttons: [
'csv', 'excel'
]
});
Use make(true) with false to render HTML (for inline editing):
$table = $datatables->of($adapter)
->make(false); // Returns HTML string
Render in Twig:
<div class="table-responsive">
{{ table|raw }}
</div>
LengthAwarePaginatorAdapter for Eloquent collections:
$adapter = new LengthAwarePaginatorAdapter(User::paginate(10));
DoctrinePaginatorAdapter:
$adapter = new DoctrinePaginatorAdapter($doctrine->getRepository(User::class)->findAll());
Extend AbstractAdapter for custom data sources:
use Omines\DataTablesBundle\DataTables\AbstractAdapter;
class ApiAdapter extends AbstractAdapter
{
public function getData()
{
$response = Http::get('https://api.example.com/users');
return $response->json();
}
public function count()
{
return Http::get('https://api.example.com/users/count')->json()['total'];
}
}
Pass the table to Twig for dynamic rendering:
return $this->render('users/_table.html.twig', [
'table' => $table->make(false),
]);
serverSide: true.count() and getData() correctly. For Eloquent, use LengthAwarePaginatorAdapter:
$adapter = new LengthAwarePaginatorAdapter(User::query());
makeColumn():
$table->addColumn('id', 'ID')
->addColumn('name', 'Name')
->addColumn('email', 'Email');
search.value) ignores custom filters.$table->add('global_search', function($query) {
$search = $this->getParameter('search.value');
return $query->where(function($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
});
count() returns the total record count, not per-page count.Add to config/packages/omines_datatables.yaml:
debug: true
Logs will show raw DataTables parameters and responses.
Dump $this->getParameters() in your controller to verify DataTables sends the expected data:
dump($datatables->getParameters());
Use browser DevTools to inspect:
draw, start, length, and columns parameters.Override the JSON response format:
$table = $datatables->of($adapter)
->make(true)
->setResponseFormatter(function($data) {
return [
'data' => $data['data'],
'meta' => [
'total' => $data['recordsTotal'],
'filtered' => $data['recordsFiltered'],
],
];
});
Listen to DataTables events (e.g., preDraw, draw):
$('#users-table').on('preDraw', function() {
console.log('Table about to redraw');
});
Use Twig functions to render columns dynamically:
{#
In your Twig template:
#}
{% macro renderCell(value, row) %}
{% if row.active %}
<span class="badge badge-success">Active</span>
{% else %}
<span class="badge badge-danger">Inactive</span>
{% endif %}
{% endmacro %}
For Symfony API Platform, combine with ApiPlatformBundle:
use Omines\DataTablesBundle\DataTables\ApiPlatformAdapter;
$adapter = new ApiPlatformAdapter($context->getCollectionOperation());
$table = $datatables->of($adapter)->make(true);
->setServerSide(true) for large datasets.add():
$table->add('export', function($query) {
return $query->select('id', 'name'); // Only fetch these columns
});
$adapter = new CachedAdapter($originalAdapter, new FilesystemCache());
How can I help you explore Laravel packages today?