diff --git a/samples/PivotTable/01_Create_PivotTable.php b/samples/PivotTable/01_Create_PivotTable.php new file mode 100644 index 000000000..36443d4e6 --- /dev/null +++ b/samples/PivotTable/01_Create_PivotTable.php @@ -0,0 +1,62 @@ +log('Create new Spreadsheet object'); +$spreadsheet = new Spreadsheet(); +$spreadsheet->getProperties() + ->setCreator('PhpSpreadsheet') + ->setTitle('PhpSpreadsheet PivotTable Sample') + ->setCategory('PivotTable'); + +// Fill a worksheet with the source data for the pivot table. +$helper->log('Add source data'); +$dataSheet = $spreadsheet->getActiveSheet(); +$dataSheet->setTitle('Data'); +$dataSheet->fromArray( + [ + ['Region', 'Product', 'Quarter', 'Amount'], + ['East', 'Widget', 'Q1', 100], + ['East', 'Gadget', 'Q1', 200], + ['West', 'Widget', 'Q1', 150], + ['West', 'Gadget', 'Q1', 250], + ['East', 'Widget', 'Q2', 120], + ['East', 'Gadget', 'Q2', 220], + ['West', 'Widget', 'Q2', 170], + ['West', 'Gadget', 'Q2', 270], + ], + null, + 'A1' +); + +// Create a worksheet to hold the pivot table. +$pivotSheet = $spreadsheet->createSheet(); +$pivotSheet->setTitle('PivotTable'); + +// Build a pivot table: +// rows = Region +// columns = Product +// values = Sum of Amount +$helper->log('Build pivot table'); +$builder = new PivotTableBuilder($dataSheet, 'A1:D9'); +$builder + ->addRowField('Region') + ->addColumnField('Product') + ->addDataField('Amount', PivotField::SUBTOTAL_SUM); + +$pivotTable = $builder->build($pivotSheet, 'A3', 'SalesByRegion'); + +$helper->log('Pivot table "' . $pivotTable->getName() . '" created at ' . $pivotTable->getLocation()); +$helper->log( + 'The pivot table is written with refresh-on-load set, so the spreadsheet ' + . 'application fills in the summarised values when the file is opened.' +); + +// Pivot tables are an Xlsx feature. +$helper->write($spreadsheet, __FILE__, ['Xlsx']); diff --git a/samples/PivotTable/02_PivotTable_Page_Filter.php b/samples/PivotTable/02_PivotTable_Page_Filter.php new file mode 100644 index 000000000..69d857186 --- /dev/null +++ b/samples/PivotTable/02_PivotTable_Page_Filter.php @@ -0,0 +1,53 @@ +log('Create new Spreadsheet object'); +$spreadsheet = new Spreadsheet(); +$spreadsheet->getProperties()->setTitle('PhpSpreadsheet PivotTable Page Filter Sample'); + +$helper->log('Add source data'); +$dataSheet = $spreadsheet->getActiveSheet(); +$dataSheet->setTitle('Data'); +$dataSheet->fromArray( + [ + ['Region', 'Product', 'Quarter', 'Amount'], + ['East', 'Widget', 'Q1', 100], + ['East', 'Gadget', 'Q1', 200], + ['West', 'Widget', 'Q1', 150], + ['West', 'Gadget', 'Q1', 250], + ['East', 'Widget', 'Q2', 120], + ['East', 'Gadget', 'Q2', 220], + ['West', 'Widget', 'Q2', 170], + ['West', 'Gadget', 'Q2', 270], + ], + null, + 'A1' +); + +$pivotSheet = $spreadsheet->createSheet(); +$pivotSheet->setTitle('PivotTable'); + +// The Quarter field is placed on the page axis, becoming a report filter that +// the user can switch between "(All)", "Q1" and "Q2" in the spreadsheet app. +$helper->log('Build pivot table with a page (report filter) field'); +$builder = new PivotTableBuilder($dataSheet, 'A1:D9'); +$builder + ->addPageField('Quarter') + ->addRowField('Region') + ->addColumnField('Product') + ->addDataField('Amount', PivotField::SUBTOTAL_SUM); + +$pivotTable = $builder->build($pivotSheet, 'A4', 'SalesFilteredByQuarter'); + +$helper->log('Page fields: ' . implode(', ', array_map( + static fn ($field) => $field->getName(), + $pivotTable->getPageFields() +))); + +$helper->write($spreadsheet, __FILE__, ['Xlsx']); diff --git a/samples/PivotTable/03_PivotTable_Grouping.php b/samples/PivotTable/03_PivotTable_Grouping.php new file mode 100644 index 000000000..a64eee8d9 --- /dev/null +++ b/samples/PivotTable/03_PivotTable_Grouping.php @@ -0,0 +1,61 @@ +log('Create new Spreadsheet object'); +$spreadsheet = new Spreadsheet(); +$spreadsheet->getProperties()->setTitle('PhpSpreadsheet PivotTable Grouping Sample'); + +$helper->log('Add source data'); +$dataSheet = $spreadsheet->getActiveSheet(); +$dataSheet->setTitle('Data'); +$dataSheet->fromArray( + [ + ['Customer', 'Age', 'OrderDate', 'Amount'], + ['Alice', 23, '2024-01-15', 100], + ['Bob', 37, '2024-06-20', 150], + ['Carol', 45, '2025-02-10', 200], + ['Dan', 51, '2025-11-05', 250], + ['Erin', 29, '2024-09-01', 175], + ['Frank', 62, '2025-07-22', 300], + ], + null, + 'A1' +); + +// --- Numeric range grouping: bucket Age into bands of 10 (20-30, 30-40, ...) --- +$helper->log('Build pivot table grouping Age into numeric ranges'); +$agePivotSheet = $spreadsheet->createSheet(); +$agePivotSheet->setTitle('ByAgeRange'); + +$ageBuilder = new PivotTableBuilder($dataSheet, 'A1:D7'); +$ageBuilder + ->groupFieldByNumericRange('Age', 10, 20, 70) + ->addRowField('Age') + ->addDataField('Amount', PivotField::SUBTOTAL_SUM); +$ageBuilder->build($agePivotSheet, 'A3', 'SalesByAgeRange'); + +// --- Date grouping: group OrderDate by quarter --- +$helper->log('Build pivot table grouping OrderDate by quarter'); +$datePivotSheet = $spreadsheet->createSheet(); +$datePivotSheet->setTitle('ByQuarter'); + +$dateBuilder = new PivotTableBuilder($dataSheet, 'A1:D7'); +$dateBuilder + ->groupFieldByDate('OrderDate', PivotFieldGroup::GROUP_BY_QUARTERS) + ->addRowField('OrderDate') + ->addDataField('Amount', PivotField::SUBTOTAL_SUM); +$dateBuilder->build($datePivotSheet, 'A3', 'SalesByQuarter'); + +$helper->log( + 'Grouping is written into the pivot cache; the spreadsheet application ' + . 'materialises the buckets when it refreshes the pivot on open.' +); + +$helper->write($spreadsheet, __FILE__, ['Xlsx']); diff --git a/samples/PivotTable/04_Read_PivotTable.php b/samples/PivotTable/04_Read_PivotTable.php new file mode 100644 index 000000000..7c46f0c1a --- /dev/null +++ b/samples/PivotTable/04_Read_PivotTable.php @@ -0,0 +1,90 @@ +log('Create a workbook containing a pivot table'); +$spreadsheet = new Spreadsheet(); +$dataSheet = $spreadsheet->getActiveSheet(); +$dataSheet->setTitle('Data'); +$dataSheet->fromArray( + [ + ['Region', 'Product', 'Amount'], + ['East', 'Widget', 100], + ['West', 'Widget', 150], + ['East', 'Gadget', 200], + ['West', 'Gadget', 250], + ], + null, + 'A1' +); +$pivotSheet = $spreadsheet->createSheet(); +$pivotSheet->setTitle('PivotTable'); + +$builder = new PivotTableBuilder($dataSheet, 'A1:C5'); +$builder + ->addRowField('Region') + ->addColumnField('Product') + ->addDataField('Amount', PivotField::SUBTOTAL_SUM); +$builder->build($pivotSheet, 'A3', 'SalesByRegion'); + +$tempFile = $helper->getTemporaryFilename('xlsx'); +(new XlsxWriter($spreadsheet))->save($tempFile); +$spreadsheet->disconnectWorksheets(); + +// Now read the file back and inspect its pivot tables. +$helper->log('Load the workbook and inspect its pivot tables'); +$loaded = (new XlsxReader())->load($tempFile); + +foreach ($loaded->getWorksheetIterator() as $worksheet) { + foreach ($worksheet->getPivotTableCollection() as $pivotTable) { + $helper->log(sprintf( + 'Pivot table "%s" on sheet "%s" at %s', + $pivotTable->getName(), + $worksheet->getTitle(), + $pivotTable->getLocation() + )); + + $cache = $pivotTable->getCacheDefinition(); + if ($cache !== null) { + $helper->log(sprintf( + ' Source: %s!%s', + (string) $cache->getSourceWorksheet(), + (string) $cache->getSourceRange() + )); + } + + $helper->log(' Row fields: ' . implode(', ', names($pivotTable->getRowFields()))); + $helper->log(' Column fields: ' . implode(', ', names($pivotTable->getColumnFields()))); + $helper->log(' Page fields: ' . implode(', ', names($pivotTable->getPageFields()))); + + foreach ($pivotTable->getDataFields() as $dataField) { + $helper->log(sprintf( + ' Value field: %s (%s)', + $dataField->getName(), + (string) $dataField->getSubtotal() + )); + } + } +} + +$loaded->disconnectWorksheets(); +@unlink($tempFile); + +/** + * @param PivotField[] $fields + * + * @return string[] + */ +function names(array $fields): array +{ + return array_map(static fn (PivotField $field): string => $field->getName(), $fields); +}