Skip to content

AbstractDataTable

AbstractDataTable

AbstractDataTable is the recommended way to create reusable, configurable tables with built-in support for Doctrine entities and server-side processing.

Overview

The class wires together:

  • DataTable: ID, columns, and extensions
  • DataProvider: Fetches rows (Doctrine or array-based)
  • RowMapper: Transforms entities into frontend-compatible arrays

Quick Start with #[AsDataTable]

For Doctrine-backed tables, use the #[AsDataTable] attribute:

use App\Entity\User;
use Pentiminax\UX\DataTables\Attribute\AsDataTable;
use Pentiminax\UX\DataTables\Model\AbstractDataTable;
use Pentiminax\UX\DataTables\Column\TextColumn;
use Pentiminax\UX\DataTables\Column\NumberColumn;
#[AsDataTable(User::class)]
final class UsersDataTable extends AbstractDataTable
{
public function configureColumns(): iterable
{
yield NumberColumn::new('id', 'ID');
yield TextColumn::new('lastName', 'Last name');
yield TextColumn::new('firstName', 'First name');
yield TextColumn::new('email', 'Email');
}
protected function mapRow(mixed $item): array
{
/** @var User $item */
return [
'id' => $item->getId(),
'lastName' => $item->getLastName(),
'firstName' => $item->getFirstName(),
'email' => $item->getEmail(),
];
}
}

The attribute automatically creates a DoctrineDataProvider configured with:

  • Your entity class
  • The rowMapper() method
  • The queryBuilderConfigurator() method

Using in a Controller

use App\DataTables\UsersDataTable;
use Pentiminax\UX\DataTables\DataTableRequest\DataTableRequest;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class UsersController extends AbstractController
{
#[Route('/users', name: 'app_users')]
public function index(UsersDataTable $table, Request $request): Response
{
// Handle Ajax requests for server-side processing
$table->handleRequest($request);
if ($table->isRequestHandled()) {
return $table->getResponse();
}
// For client-side tables, fetch data before rendering
if (!$table->getDataTable()->isServerSide()) {
$table->fetchData(DataTableRequest::fromRequest($request));
}
return $this->render('users/index.html.twig', [
'table' => $table->getDataTable(),
]);
}
}

Customizing the Query

Query Builder Configurator

Filter or modify the Doctrine query:

use Doctrine\ORM\QueryBuilder;
use Pentiminax\UX\DataTables\DataTableRequest\DataTableRequest;
#[AsDataTable(User::class)]
final class ActiveUsersDataTable extends AbstractDataTable
{
public function queryBuilderConfigurator(QueryBuilder $qb, DataTableRequest $request): QueryBuilder
{
return $qb
->andWhere('e.active = :active')
->setParameter('active', true)
->andWhere('e.deletedAt IS NULL');
}
public function configureColumns(): iterable
{
// ...
}
}

Dynamic Filtering

Use request parameters for dynamic filters:

public function queryBuilderConfigurator(QueryBuilder $qb, DataTableRequest $request): QueryBuilder
{
// Access custom parameters
if ($roleId = $request->getCustomParameter('role')) {
$qb->andWhere('e.role = :role')
->setParameter('role', $roleId);
}
return $qb;
}

Configuring the DataTable

Table Options

use Pentiminax\UX\DataTables\Model\DataTable;
#[AsDataTable(User::class)]
final class UsersDataTable extends AbstractDataTable
{
public function configureDataTable(DataTable $table): DataTable
{
return $table
->pageLength(25)
->searching(true)
->ordering(handler: true, indicators: true)
->serverSide(true)
->processing(true);
}
public function configureColumns(): iterable
{
// ...
}
}

Extensions

use Pentiminax\UX\DataTables\Model\Extensions\ButtonsExtension;
use Pentiminax\UX\DataTables\Model\Extensions\SelectExtension;
use Pentiminax\UX\DataTables\Model\DataTableExtensions;
public function configureExtensions(DataTableExtensions $extensions): DataTableExtensions
{
return $extensions
->add(new ButtonsExtension([ButtonType::CSV, ButtonType::EXCEL]))
->add(new SelectExtension(SelectStyle::MULTI));
}

Or configure individual extensions:

public function configureButtonsExtension(ButtonsExtension $ext): ButtonsExtension
{
return $ext->buttons([
ButtonType::COPY,
ButtonType::CSV,
ButtonType::PDF,
]);
}
public function configureSelectExtension(SelectExtension $ext): SelectExtension
{
return $ext->style(SelectStyle::SINGLE);
}

Available Methods

Configuration Methods

MethodDescription
configureDataTable(DataTable $table)Configure table options
configureColumns()Define columns (required)
configureExtensions(DataTableExtensions $ext)Add extensions
configureButtonsExtension(ButtonsExtension $ext)Configure Buttons extension
configureSelectExtension(SelectExtension $ext)Configure Select extension
configureColumnControlExtension(...)Configure ColumnControl extension

Data Methods

MethodDescription
getDataProvider()Return custom DataProvider
fetchData(DataTableRequest $request)Fetch data using the provider
mapRow(mixed $item)Map a single entity to array
rowMapper()Get RowMapperInterface instance
queryBuilderConfigurator(QueryBuilder $qb, ...)Modify Doctrine query

Request Handling

MethodDescription
handleRequest(Request $request)Handle Ajax requests
isRequestHandled()Check if request was handled
getResponse()Get JsonResponse for Ajax
getDataTable()Get the configured DataTable

Manual Data Provider

Override getDataProvider() for custom data sources:

use Pentiminax\UX\DataTables\Contracts\DataProviderInterface;
use Pentiminax\UX\DataTables\DataProvider\ArrayDataProvider;
#[AsDataTable(User::class)] // Attribute is ignored when getDataProvider() is defined
final class CustomUsersDataTable extends AbstractDataTable
{
public function getDataProvider(): ?DataProviderInterface
{
// Static data
$rows = [
['id' => 1, 'name' => 'John Doe'],
['id' => 2, 'name' => 'Jane Smith'],
];
return new ArrayDataProvider($rows, $this->rowMapper());
}
protected function mapRow(mixed $item): array
{
// For arrays, just return as-is
return is_array($item) ? $item : (array) $item;
}
}

Core Interfaces

DataProviderInterface

interface DataProviderInterface
{
public function fetchData(DataTableRequest $request): DataTableResult;
}

RowMapperInterface

interface RowMapperInterface
{
public function map(mixed $row): array;
}

DataTableRequest

Contains pagination, search, ordering, and column metadata from the client request.

DataTableResult

Wraps data payload with total and filtered counts:

new DataTableResult(
recordsTotal: 1000,
recordsFiltered: 150,
data: $rows
);