6 Commits

Author SHA1 Message Date
oleibman 08eb18d7ae Unset PivotTableCollection at Destruct, plus some Performance Tweaks 2026-08-03 19:05:00 -07:00
saifulislamferoz 2e4cc54a0d Fix corrupt date-grouped pivot table and import ordering
Excel reported sample 3's ByQuarter sheet as corrupt: a date field
group (rangePr groupBy="quarters") was emitted without the date bounds
Excel requires. When no explicit start/end date is supplied, fall back
to the sentinel range (1900-01-01 .. 9999-12-31) so both the sharedItems
(minDate/maxDate) and the rangePr (startDate/endDate) are always present,
matching the <1/1/1900 / >12/31/9999 out-of-range group items.

Also fixes ordered_imports in Worksheet.php (php-cs-fixer CI failure):
the PivotTable\PivotTable use statement is moved before the Sparkline
imports.
2026-08-03 16:16:35 +06:00
saifulislamferoz 5efba3ee64 Increase pivot table test coverage
Cover the previously-unexercised code paths flagged by Coveralls:
- writer: month and year date grouping, and a fractional numeric interval
  (the non-integer num() path);
- reader: reading an explicit dataField subtotal attribute back from a saved
  file;
- worksheet: the getPivotTables() alias, addPivotTable(), case-insensitive
  getPivotTableByName(), the no-match lookup, and removePivotTableCollection().
2026-08-03 11:52:58 +06:00
saifulislamferoz e5b0477559 Fix PHPStan errors in pivot table tests
Use getSheetByNameOrThrow() instead of the nullable getSheetByName() for
chained calls, assert non-null before dereferencing getCacheDefinition() and
getPivotTableByName(), and replace an inline array_map closure with a typed
helper so PHPStan (level 8) is satisfied.
2026-08-03 11:52:58 +06:00
saifulislamferoz c899e5cb75 Add pivot page (filter) fields and numeric/date grouping
Extends PivotTableBuilder with the two remaining common pivot capabilities.

Page fields: addPageField() places a field on the page (report filter) axis,
and the writer emits a <pageFields> section (in its correct schema position,
after colFields and before dataFields) with the field marked axis="axisPage".

Grouping: a new Worksheet\PivotTable\PivotFieldGroup value object captures how a
field is grouped, and the builder gains groupFieldByNumericRange() (fixed-width
buckets between a start and end number) and groupFieldByDate() (group a date
field by a calendar unit such as quarters or months). The grouping is written
into the pivot cache definition as a <fieldGroup> with <rangePr> and computed
<groupItems>; with refresh-on-load the spreadsheet application materialises the
buckets when it opens the file.

The generated cache definition, workbook wiring and content types remain a
structurally consistent OPC package, and pivots with page fields or grouping
read back into the object model. Tests cover page-field placement/output,
numeric range grouping, date quarter grouping, and validation of unknown
fields.
2026-08-03 11:52:55 +06:00
saifulislamferoz 5aa558b6d6 Add pivot table creation via PivotTableBuilder (Xlsx)
Adds the ability to create a new pivot table from a range of source data,
building on the existing read model and round-trip preservation.

New Worksheet\PivotTable\PivotTableBuilder provides a fluent API: point it at a
source worksheet and range (its first row supplies the field names), place
fields on the row/column axes and add value fields with an aggregation
function, then build() produces a PivotTable and registers it on a target
sheet. Source field names are resolved from the header row (RichText/inline
string headers included), and distinct values are collected for the cache
definition's sharedItems.

The model gains what generation needs: PivotTable::isGenerated(), per-field
data-field captions, subtotal-function constants on PivotField, and sharedItems
on PivotCacheDefinition.

Writer: a new Writer\Xlsx\PivotTable generates the pivotTableDefinition,
pivotCacheDefinition (with refreshOnLoad set) and an empty pivotCacheRecords
part, plus their rels. On save, generated pivot tables are assigned indices
that continue past any preserved pivot parts (so a workbook can mix loaded and
new pivots without collision) and are wired into the worksheet relationships,
workbook <pivotCaches> registry, workbook relationships and content types via
the same unparsed-data path used for preservation. Values are computed by the
spreadsheet application on open via refresh-on-load, so we don't reimplement
the aggregation engine.

Output is a structurally consistent OPC package and reads back into the object
model. Tests cover building, validation errors, the generated parts and
wiring, aggregation attributes, and mixing preserved with generated pivots.
2026-08-03 11:52:45 +06:00