Type to search columns, filters, options, and extensions.

↑↓ to navigate · Enter to open · Esc to close

Documentation

Column Summaries

Show totals, averages, minimums, maximums, and counts in the table footer

A column can show a figure in the table footer with summarize(). The summary covers every row matching the current search and filters, not only the visible page.

Declaring Summaries

use App\Entity\Order;
use Pentiminax\UX\DataTables\Attribute\AsDataTable;
use Pentiminax\UX\DataTables\Column\DateColumn;
use Pentiminax\UX\DataTables\Column\MoneyColumn;
use Pentiminax\UX\DataTables\Column\NumberColumn;
use Pentiminax\UX\DataTables\Column\TextColumn;
use Pentiminax\UX\DataTables\Model\AbstractDataTable;
use Pentiminax\UX\DataTables\Model\DataTable;
use Pentiminax\UX\DataTables\Model\Summary\Average;
use Pentiminax\UX\DataTables\Model\Summary\Count;
use Pentiminax\UX\DataTables\Model\Summary\Max;
use Pentiminax\UX\DataTables\Model\Summary\Min;
use Pentiminax\UX\DataTables\Model\Summary\Sum;

#[AsDataTable(Order::class)]
final class OrdersDataTable extends AbstractDataTable
{
    public function configureDataTable(DataTable $table): DataTable
    {
        return $table->serverSide();
    }

    public function configureColumns(): iterable
    {
        yield TextColumn::new('reference', 'Reference')->summarize(Count::new('Orders'));
        yield MoneyColumn::new('total', 'Total')
            ->currency('EUR')
            ->summarize(Sum::new('Revenue'), Average::new());
        yield NumberColumn::new('items', 'Items')->summarize(Min::new('Smallest'), Max::new('Largest'));
        yield DateColumn::new('createdAt', 'Created')->summarize(Count::new('Dated'));
    }
}
SummarizerResultColumn
SumTotal of the valuesNumeric
AverageMean of the valuesNumeric
Min / MaxSmallest and largest valueNumeric
CountNumber of non-null values (empty strings included)Any

Each summarizer takes an optional label (Sum::new('Revenue')). Without one, the translated default is used (summary.sum, summary.avg, and so on, in the DataTables domain). A column holds one summarizer per type.

Null values are ignored, as SQL does. MoneyColumn figures are formatted like the cells of the column. summarize() throws an InvalidArgumentException for a TemplateColumn or ActionColumn, and for Sum, Average, Min or Max on a non-numeric column (dates included: they are formatted differently in SQL, PHP and the browser, so only Count applies).

How It Is Computed

  • Server-side Doctrine tables run one extra aggregate query built from the same query as the rows (customizeQueryBuilder() scope, search, and filters) without pagination or ordering. The response gains a summaries key only when a column declares a summary. The query matches the root entities first, so a to-many join or a search on a collection never inflates a total.
  • Client-side tables compute in the browser from the rows matching the current search, so a virtual column works there. The browser reads the displayed values: rendering throws a LogicException when a column combines formatValueUsing() with Sum, Average, Min, or Max, because "1,234.50" is not a number. Make the table server-side or keep Count only.
  • ArrayDataProvider aggregates in memory.
  • API Platform tables cannot aggregate: rendering throws a LogicException when a column declares a summary.
  • Custom providers return the figures through the summaries argument of DataTableResult, shaped ['columnName' => ['sum' => 120, 'count' => 4]].

On a server-side table, the summarized column must map a Doctrine field (or a relation path such as region.quota). A virtual column assembled in mapRow(), or a path crossing a to-many association such as notes.text, raises a LogicException on the first request, as does an entity with a composite identifier. A scope with a HAVING clause binds the matching identifiers as one list, so keep such scopes to a moderate number of rows.

Sums of DECIMAL columns are returned as floats, so very large totals or many decimals can lose precision; format them with MoneyColumn or round in your own provider.

Summaries never include a column the user may not see (setPermission()).

The bundle creates the <tfoot> when the table has none, with one th scope="col" cell per column, and leaves columns without a summary empty. Labels and values are inserted as text. Style it with the --dt-summary-surface, --dt-summary-border, --dt-summary-text, and --dt-summary-text-muted CSS variables, which follow the filter palette and its dark mode.