The DataTable component of the AdminBundle simplifies the creation of JSON responses compatible with the DataTables jQuery plugin. It provides a Fluent Interface to configure columns, filters, and rendering.
The recommended way to use the component is to handle both the HTML page and the JSON data in a single controller action. This avoids duplicating column definitions.
use App\Entity\Event;
use Aropixel\AdminBundle\Component\DataTable\DataTableFactory;
#[Route("/", name: "index", methods: ["GET"])]
public function index(DataTableFactory $dataTableFactory): Response
{
return $dataTableFactory
->create(Event::class)
->setColumns([
['label' => 'Title', 'orderBy' => 'title'],
['label' => 'Date', 'orderBy' => 'startDate', 'style' => 'width:200px;'],
['label' => '', 'orderBy' => '', 'class' => 'no-sort'],
])
->searchIn(['title'])
->renderJson(fn(Event $event) => [
$event->getTitle(),
$event->getStartDate()->format('d/m/Y'),
$this->renderView('admin/event/_actions.html.twig', ['item' => $event]),
])
->render('admin/event/index.html.twig');
}
renderJson(): Stores the transformer. If it's an AJAX request, it sets the JSON content.render(): Stores the template and parameters. If it's not an AJAX request, it renders the Twig template.
Both methods return the DataTable object itself, which extends Symfony's Response. This allows you to chain them in any order and return the result directly.Columns can be defined in three ways:
DataTableColumn).DataTableColumn instance.The array can contain the following keys:
label: Label displayed in the header.orderBy: Entity field used for sorting (if applicable). Supports aliases if joins are used.style: CSS style attribute to add to the cell.class: CSS classes to add to the cell (e.g., no-sort to disable sorting on a column). You can also use alignment classes like text-start, text-center, or text-end.data: Associative array of custom data- attributes to add to the <th> tag (e.g., ['type' => 'date-euro'] becomes data-type="date-euro").You can define the default sorting for the table:
$dataTableFactory
->create(Event::class)
->setOrderColumn(2) // Index of the column (starting from 0)
->setOrderDirection('desc') // 'asc' or 'desc'
// ...
These values are automatically rendered in the Twig template as data-order-column and data-order-direction attributes.
The component can automatically handle search (LIKE clauses) and sorting (ORDER BY) if you provide the searchable fields:
$dataTableFactory
->create(Event::class)
->setColumns([
['label' => 'Title', 'field' => 'title'],
['label' => 'Date', 'field' => 'createdAt'],
['label' => '', 'field' => '', 'class' => 'no-sort'],
])
->searchIn(['title', 'subTitle']) // Enable automatic search on these fields
// ...
By default, the component uses the DefaultDataTableRepository. If searchIn() is used or if no custom repository method is specified, it will:
QueryBuilder for the entity.LIKE conditions on the fields specified in searchIn().ORDER BY based on the clicked column and its orderBy configuration.The component supports easy joins and the use of aliases in searchIn() and orderBy.
$dataTableFactory
->create(Event::class)
->join('category', 'c') // Left join event.category with alias 'c'
->setColumns([
['label' => 'Title', 'orderBy' => 'title'],
['label' => 'Category', 'orderBy' => 'c.name'], // Use alias in orderBy
['label' => '', 'orderBy' => '', 'class' => 'no-sort'],
])
->searchIn(['title', 'c.name']) // Use alias in searchIn
// ...
When using join(property, alias), the component will automatically perform a leftJoin. You can then use the alias in any orderBy column or in the searchIn() array.
If you need complex logic, you can change the method called in your Doctrine repository:
$dataTableFactory
->create(Event::class)
->useRepositoryMethod('getArchivedEventsQuery')
// ...
Similar to the Select2 component, you can add dynamic constraints:
$dataTableFactory
->create(Event::class)
->filter(function(QueryBuilder $qb) {
$qb->andWhere('e.active = :active')->setParameter('active', true);
})
// ...
setColumns(array $columns): selfDefines the set of columns. Overwrites any previously added columns.
addColumn(array|DataTableColumn $column): selfAdds a column to the existing list.
addColumnsIf(bool $condition, array $columns): selfAdds multiple columns only if the condition is met.
useRepositoryMethod(string $methodName): selfDefines the Doctrine repository method to call to retrieve the QueryBuilder.
filter(callable $filter): selfAdds a callback function to modify the QueryBuilder before executing the query.
searchIn(array $fields): selfEnables automatic search on the specified entity fields. Supports aliases if joins are used.
join(string $property, string $alias): selfAdds a leftJoin to the QueryBuilder. The property is the relation name on the main entity, and alias is the alias to use in orderBy and searchIn.
setOrderColumn(?int $index): selfSets the default column index for sorting.
setOrderDirection(?string $direction): selfSets the default sorting direction (asc or desc).
renderJson(callable $transformer): selfConfigures the data transformer. If the current request is an AJAX request from DataTables, it will also prepare the JSON content for the response.
render(string $template, array $parameters = []): selfConfigures the template to use. If the current request is NOT an AJAX request, it will also render the template and prepare the HTML content for the response.
By default, the DataTableFactory creates an object in XHR (AJAX) mode.
The table is loaded via AJAX. If you use the single-action approach, the AJAX URL is automatically the current URL.
The table is rendered directly with provided data.
return $dataTableFactory
->create(Event::class, mode: DataTableInterface::MODE_CLASSIC)
->setItems($events)
->setColumns([...])
->render('admin/event/list.html.twig');
To simplify the creation of action menus in columns, a Twig macro is available.
{% extends '[@AropixelAdmin](https://github.com/AropixelAdmin)/List/datatable.html.twig' %}
{% block datatable_row %}
<tr>
<td>{{ item.title }}</td>
<td>{{ item.startDate|date('d/m/Y') }}</td>
<td class="text-right">
{{ list.actions(item, path('admin_event_edit', {id: item.id}), path('admin_event_delete', {id: item.id})) }}
</td>
</tr>
{% endblock %}
The [@AropixelAdmin](https://github.com/AropixelAdmin)/List/datatable.html.twig template automatically adapts to the dataTable object and its mode.
If you use XHR mode with the single-action pattern, you don't even need to provide ajax_url.
{% extends '[@AropixelAdmin](https://github.com/AropixelAdmin)/List/datatable.html.twig' %}
You can still manually define headers and body if you do not pass a dataTable object.
{% extends '[@AropixelAdmin](https://github.com/AropixelAdmin)/List/datatable.html.twig' %}
{% block datatable_header %}
<th>Title</th>
{% endblock %}
{% block datatable_body %}
{% for event in events %}
<tr>
<td>{{ event.title }}</td>
</tr>
{% endfor %}
{% endblock %}
How can I help you explore Laravel packages today?