Data Providers & Row Mappers
With AbstractDataTable, providers are resolved internally and row mapping is applied through a
pipeline. The usual extension points are:
createDataProvider()for custom providersmapRow()for domain-to-array mappingcreateRowMapper()when you need the built-in mapping/template/action pipeline instance
DataProviderInterface
use Pentiminax\UX\DataTables\DataTableRequest\DataTableRequest;
use Pentiminax\UX\DataTables\Model\DataTableResult;
interface DataProviderInterface
{
public function fetchData(DataTableRequest $request): DataTableResult;
}
Built-in providers:
| Provider | Use case |
|---|---|
| `DoctrineDataProvider` | Doctrine ORM-backed datasets |
| `ArrayDataProvider` | In-memory or preloaded arrays |
RowMapperInterface
interface RowMapperInterface
{
public function map(mixed $row): array;
}
Built-in mappers and processors:
| Mapper | Use case |
|---|---|
| `DefaultRowMapper` | Default row-to-array mapping behavior |
| `ClosureRowMapper` | Custom mapping logic via closure |
| `RowProcessingPipeline` | Composable pipeline used by `AbstractDataTable` — stages added via `->add(RowStageInterface)` |
RowStageInterface
Each stage in RowProcessingPipeline implements RowStageInterface:
interface RowStageInterface
{
public function process(array $mappedRow, mixed $originalRow, array $columns): array;
}
Built-in stages applied by default (in order):
| Stage | Responsibility |
|---|---|
| `NormalizationStage` | Dotted-path resolution, `DateColumn` formatting, `Stringable` casting |
| `TemplateRenderingStage` | Renders `TemplateColumn` cells via Twig (only if renderer is available) |
| `ActionResolutionStage` | Resolves `ActionColumn` URLs into `__ux_datatables_actions` key |
Custom stage example:
use Pentiminax\UX\DataTables\Contracts\RowStageInterface;
final class UpperCaseTitleStage implements RowStageInterface
{
public function process(array $mappedRow, mixed $originalRow, array $columns): array
{
if (isset($mappedRow['title'])) {
$mappedRow['title'] = strtoupper($mappedRow['title']);
}
return $mappedRow;
}
}
protected function createRowMapper(): RowMapperInterface
{
return parent::createRowMapper()->add(new UpperCaseTitleStage());
}
DataTableResult
new DataTableResult(
recordsTotal: 1000,
recordsFiltered: 150,
data: $rows,
);
When To Implement Custom Types
- custom domain filters with non-Doctrine backends
- APIs requiring specific output shape
- performance tuning with pre-mapped row payloads
Manual Provider Example
use Pentiminax\UX\DataTables\Contracts\DataProviderInterface;
use Pentiminax\UX\DataTables\DataProvider\ArrayDataProvider;
protected function createDataProvider(): ?DataProviderInterface
{
return new ArrayDataProvider($this->rows, $this->createRowMapper());
}
$this->createRowMapper() is the important part: it preserves the same mapping, template
rendering, and action-resolution behavior as the built-in Doctrine provider. setData() on
AbstractDataTable uses that same pipeline for inline rows.
Custom stages added to the pipeline are preserved when passing the mapper to a custom provider.