/

Grouped Rows, Sums & Reports

Collapsible multi-level row grouping with subtotals, plus a grand-total sum() footer over the full filtered result set.

group_by_columns(): nesting levels

public function group_by_columns(string $columns): self

A comma-separated column list groups the list view's rows under collapsible header rows - the Nth column becomes the Nth nesting level:

$xcrud->group_by_columns('customerNumber,paymentDate');

This groups rows first by customerNumber (level 1), then by paymentDate within each customer (level 2). Every column must be one of the table's whitelisted columns. The grid is automatically re-sorted so same-group rows stay contiguous - group columns sort first, with whatever column is clicked to sort applying as a secondary tiebreaker within the innermost group.

A date/datetime column used as a group level doesn't group by its exact value (which would make almost every row its own group) - it buckets into relative labels instead: "Today", "Yesterday", "N Days Ago", "Last Week", "This Month", "Last Month", then "{Month}" for the current year and "{Month} {Year}" further back.

group_sum_columns(): per-group subtotals

public function group_sum_columns(string $columns): self

Adds a subtotal for the named numeric column(s) to every group's header row, at every level, computed over just that group's rows on the current page:

$xcrud->group_by_columns('customerNumber');
$xcrud->group_sum_columns('amount');

Each customer's group header shows that customer's total across their rows on the current page - not the whole filtered set. Unlike sum()'s grand-total footer below, this is scoped per group, and a group can be split across pages just like group_by_columns() itself can.

group_table_collapsed(): default open/closed state

public function group_table_collapsed(bool $collapsed, ?int $level = null): self
ParameterTypeDefaultDescription
$collapsedbool-Whether groups start collapsed (hiding their rows until clicked open).
$levelint|nullnull1-indexed level to override, matching group_by_columns()'s argument order. Omit to set the default for every level with no override.
$xcrud->group_table_collapsed(true);      // every level starts collapsed...
$xcrud->group_table_collapsed(false, 1);  // ...except level 1

Every group can still be toggled open/closed by clicking it, regardless of its starting state.

Customizing subtotal labels

public function group_sum_show_label(bool $show = true): self
public function group_sum_label(string $field, string $label): self

group_sum_show_label(false) drops the "Label:" prefix from every summed column's subtotal, showing just the value (e.g. "23,000" instead of "Amount: 23,000"). group_sum_label() overrides one column's label specifically, instead of its own label()/auto-humanized name:

$xcrud->group_sum_columns('amount');
$xcrud->group_sum_label('amount', 'Total Paid');

This shows "Total Paid: 23,000" on every group header. It has no effect if group_sum_show_label(false) is also set, since no label shows at all in that case.

public function sum(string $field): self

Adds a SUM() footer total for a numeric column, computed server-side over the current filtered/searched result set as a whole - not just the current page, and independent of any grouping:

$xcrud->sum('amount');

Putting it together

$xcrud->group_by_columns('paymentDate,customerNumber');
$xcrud->group_sum_columns('amount');
$xcrud->group_sum_label('amount', 'Total Paid');
$xcrud->group_table_collapsed(true);
$xcrud->group_table_collapsed(false, 1);
$xcrud->sum('amount');

Here level 1 (date buckets, usually a handful) starts expanded while level 2 (customer number, often dozens per bucket) starts collapsed, each group header shows a "Total Paid: ..." subtotal, and the grid footer shows the grand total across the whole filtered set.