Installation
composer require brown298/data-tables-bundle
Add to config/bundles.php:
Brown298\DataTablesBundle\Brown298DataTablesBundle::class => ['all' => true],
Basic Configuration
Update config/packages/brown298_data_tables.yaml (if needed) or use defaults.
First Use Case: Displaying a Table In a controller:
use Brown298\DataTablesBundle\DataTables;
use Brown298\DataTablesBundle\DataTablesFactory;
public function indexAction(DataTables $datatables, DataTablesFactory $factory)
{
$builder = $factory->createBuilder($this->getDoctrine()->getManager());
$builder->add('entity', 'App\Entity\YourEntity');
$data = $datatables->serverSide($builder);
return $this->render('your_template.html.twig', [
'data' => $data,
]);
}
Twig Template
{{ include('Brown298DataTablesBundle::table.html.twig', {
data: data,
columns: [
{ data: 'id', name: 'ID' },
{ data: 'name', name: 'Name' },
]
}) }}
$builder = $factory->createBuilder($em);
$builder
->add('entity', 'App\Entity\Product')
->addColumn('name', 'Name')
->addColumn('price', 'Price', 'currency')
->addFilter('name', 'Name')
->addOrder('createdAt', 'Created At', 'desc');
$data = $datatables->serverSide($builder);
Extend the builder for complex queries:
$builder->add('entity', 'App\Entity\Order')
->addColumn('total', 'Total', function($order) {
return $order->getTotal();
})
->addCallback('customFilter', function($query, $input) {
$query->andWhere('o.status = :status')
->setParameter('status', $input);
});
Use DataTables with Symfony Forms for dynamic filtering:
{{ form_start(form) }}
{{ form_widget(form.search) }}
{{ include('Brown298DataTablesBundle::table.html.twig', {
data: data,
form: form,
}) }}
{{ form_end(form) }}
Return JSON for SPA-like behavior:
return $datatables->ajax($builder);
add() with entity names for automatic column detection.addQuery():
$builder->addQuery('SELECT p, c FROM App\Entity\Product p JOIN p.category c');
Inject a custom repository into the builder:
$builder->setRepository('App\Entity\Product', $customRepository);
Use {{ datatables() }} helper for quick table generation:
{{ datatables({
'entity': 'App\Entity\User',
'columns': [{ data: 'email', name: 'Email' }]
}) }}
Column Mismatch
data attributes in Twig match the actual entity properties or custom column names.dd($builder->getColumns()) to verify column definitions.Case Sensitivity
addFilter('name', 'Name', 'like') for case-insensitive searches.Lazy-Loading Associations
addColumn() with join:
$builder->addColumn('category.name', 'Category', 'join');
Pagination Conflicts
brown298_data_tables:
server_side: true
pagination: false
Query Logging Enable Doctrine logging to inspect generated queries:
$em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
Builder Inspection Dump the builder state before server-side processing:
dd($builder->getQuery(), $builder->getColumns());
Custom Column Types
Extend Brown298\DataTablesBundle\Column\ColumnInterface for custom rendering:
class CustomColumn extends AbstractColumn
{
public function render($value, $row)
{
return '<span class="badge">' . $value . '</span>';
}
}
Event Listeners
Subscribe to datatables.build.query or datatables.render.cell events:
services:
App\EventListener\DataTablesListener:
tags:
- { name: kernel.event_listener, event: datatables.build.query, method: onBuildQuery }
Override Templates
Copy templates/table.html.twig to templates/Brown298DataTablesBundle/table.html.twig for custom styling.
Selective Loading
Use addColumn() with select to limit loaded fields:
$builder->addColumn('name', 'Name', null, ['select' => 'p.name']);
Caching Cache server-side responses for static data:
$data = $datatables->serverSide($builder);
$this->get('cache')->save('datatables_data', $data, 3600);
Batch Processing For large datasets, implement cursor-based pagination:
$builder->addCallback('customPagination', function($query, $limit, $offset) {
$query->setFirstResult($offset)->setMaxResults($limit);
});
How can I help you explore Laravel packages today?