Merge pull request #4911 from oleibman/issue413

Chart DataTable
This commit is contained in:
oleibman
2026-06-13 03:36:44 +00:00
committed by GitHub
6 changed files with 419 additions and 1 deletions
+108
View File
@@ -0,0 +1,108 @@
<?php
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\DataTable;
//use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Theme as SpreadsheetTheme;
require __DIR__ . '/../Header.php';
/** @var PhpOffice\PhpSpreadsheet\Helper\Sample $helper */
$spreadsheet = new Spreadsheet();
// same as 33_Chart_create_area, but with 2013+ schemes
$spreadsheet->getTheme()->setThemeColorName(SpreadsheetTheme::COLOR_SCHEME_2013_2022_NAME);
$worksheet = $spreadsheet->getActiveSheet();
$sheet2 = $spreadsheet->createSheet();
$sheet2->setTitle('Sheet2');
$worksheet->fromArray(
[
['', 2010, 2011, 2012],
['Q1', 12, 15, 21],
['Q2', 56, 73, 86],
['Q3', 52, 61, 69],
['Q4', 30, 32, 0],
]
);
// Set the Labels for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$dataSeriesLabels = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$B$1', null, 1), // 2010
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // 2011
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // 2012
];
// Set the X-Axis Labels
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$xAxisTickValues = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4
];
// Set the Data values for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$dataSeriesValues = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$5', null, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', null, 4),
];
// Build the dataseries
$series = new DataSeries(
DataSeries::TYPE_AREACHART, // plotType
DataSeries::GROUPING_PERCENT_STACKED, // plotGrouping
range(0, count($dataSeriesValues) - 1), // plotOrder
$dataSeriesLabels, // plotLabel
$xAxisTickValues, // plotCategory
$dataSeriesValues // plotValues
);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
$plotArea->setDataTable(new DataTable());
// No need for Legend if using DataTable
$legend = null; //new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title = new Title('Test %age-Stacked Area Chart');
$yAxisLabel = new Title('Value ($k)');
// Create the chart
$chart = new Chart(
'chart1', // name
$title, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
null, // xAxisLabel
$yAxisLabel // yAxisLabel
);
// Set the position where the chart should appear in the worksheet
$chart->setTopLeftPosition('A1');
$chart->setBottomRightPosition('H18');
// Add the chart to the worksheet
$sheet2->addChart($chart);
$spreadsheet->setActiveSheetIndex(1);
$helper->renderChart($chart, __FILE__);
// Save Excel 2007 file
$helper->write($spreadsheet, __FILE__, ['Xlsx'], true, null, false);
+62
View File
@@ -0,0 +1,62 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Chart;
class DataTable
{
private bool $showHorizontalBorder = true;
private bool $showVerticalBorder = true;
private bool $showOutline = true;
private bool $showKeys = true;
public function getShowHorizontalBorder(): bool
{
return $this->showHorizontalBorder;
}
public function getShowVerticalBorder(): bool
{
return $this->showVerticalBorder;
}
public function getShowOutline(): bool
{
return $this->showOutline;
}
public function getShowKeys(): bool
{
return $this->showKeys;
}
public function setShowHorizontalBorder(bool $showHorizontalBorder): self
{
$this->showHorizontalBorder = $showHorizontalBorder;
return $this;
}
public function setShowVerticalBorder(bool $showVerticalBorder): self
{
$this->showVerticalBorder = $showVerticalBorder;
return $this;
}
public function setShowOutline(bool $showOutline): self
{
$this->showOutline = $showOutline;
return $this;
}
public function setShowKeys(bool $showKeys): self
{
$this->showKeys = $showKeys;
return $this;
}
}
+14
View File
@@ -38,6 +38,8 @@ class PlotArea
*/
private array $plotSeries;
private ?DataTable $dataTable = null;
/**
* Create a new PlotArea.
*
@@ -195,6 +197,18 @@ class PlotArea
return $this;
}
public function setDataTable(DataTable $dataTable): self
{
$this->dataTable = $dataTable;
return $this;
}
public function getDataTable(): ?DataTable
{
return $this->dataTable;
}
/**
* Implement PHP __clone to create a deep clone, not just a shallow copy.
*/
+34 -1
View File
@@ -8,6 +8,7 @@ use PhpOffice\PhpSpreadsheet\Chart\AxisText;
use PhpOffice\PhpSpreadsheet\Chart\ChartColor;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\DataTable;
use PhpOffice\PhpSpreadsheet\Chart\GridLines;
use PhpOffice\PhpSpreadsheet\Chart\Layout;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
@@ -137,7 +138,7 @@ class Chart
break;
case 'plotArea':
$plotAreaLayout = $XaxisLabel = $YaxisLabel = null;
$plotAreaLayout = $XaxisLabel = $YaxisLabel = $dataTable = null;
$plotSeries = $plotAttributes = [];
$catAxRead = false;
$plotNoFill = false;
@@ -358,6 +359,10 @@ class Chart
}
$plotAttributes = $this->readChartAttributes($chartDetail);
break;
case 'dTable':
$dataTable = $this->readDataTable($chartDetail);
break;
}
}
@@ -365,6 +370,11 @@ class Chart
$plotAreaLayout = new Layout();
}
$plotArea = new PlotArea($plotAreaLayout, $plotSeries);
if ($dataTable !== null) {
$plotArea->setDataTable(
$dataTable
);
}
$this->setChartAttributes($plotAreaLayout, $plotAttributes);
if ($plotNoFill) {
$plotArea->setNoFill(true);
@@ -1295,6 +1305,29 @@ class Chart
return $plotAttributes;
}
private function readDataTable(SimpleXMLElement $chartDetail): DataTable
{
$dataTable = new DataTable();
$temp = self::getAttributeBoolean($chartDetail->showHorzBorder, 'val');
if ($temp !== null) {
$dataTable->setShowHorizontalBorder($temp);
}
$temp = self::getAttributeBoolean($chartDetail->showVertBorder, 'val');
if ($temp !== null) {
$dataTable->setShowVerticalBorder($temp);
}
$temp = self::getAttributeBoolean($chartDetail->showOutline, 'val');
if ($temp !== null) {
$dataTable->setShowOutline($temp);
}
$temp = self::getAttributeBoolean($chartDetail->showKeys, 'val');
if ($temp !== null) {
$dataTable->setShowKeys($temp);
}
return $dataTable;
}
/** @param array<mixed> $plotAttributes */
private function setChartAttributes(Layout $plotArea, array $plotAttributes): void
{
+28
View File
@@ -7,6 +7,7 @@ use PhpOffice\PhpSpreadsheet\Chart\Chart as SpreadsheetChart;
use PhpOffice\PhpSpreadsheet\Chart\ChartColor;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\DataTable;
use PhpOffice\PhpSpreadsheet\Chart\Layout;
use PhpOffice\PhpSpreadsheet\Chart\Legend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
@@ -447,6 +448,10 @@ class Chart extends WriterPart
$this->writeValueAxis($objWriter, $xAxisLabel, $chartType, $id2, $id1, $catIsMultiLevelSeries, $xAxis ?? new Axis());
} else {
$this->writeCategoryAxis($objWriter, $xAxisLabel, $id1, $id2, $catIsMultiLevelSeries, $xAxis ?? new Axis());
$dataTable = $plotArea->getDataTable();
if ($dataTable !== null) {
$this->writeDataTable($objWriter, $dataTable);
}
}
$this->writeValueAxis($objWriter, $yAxisLabel, $chartType, $id1, $id2, $valIsMultiLevelSeries, $yAxis ?? new Axis());
@@ -485,6 +490,29 @@ class Chart extends WriterPart
$objWriter->endElement(); // c:plotArea
}
private function writeDataTable(XMLWriter $objWriter, DataTable $dataTable): void
{
$objWriter->startElement('c:dTable');
$objWriter->startElement('c:showHorzBorder');
$objWriter->writeAttribute('val', $dataTable->getShowHorizontalBorder() ? '1' : '0');
$objWriter->endElement();
$objWriter->startElement('c:showVertBorder');
$objWriter->writeAttribute('val', $dataTable->getShowVerticalBorder() ? '1' : '0');
$objWriter->endElement();
$objWriter->startElement('c:showOutline');
$objWriter->writeAttribute('val', $dataTable->getShowOutline() ? '1' : '0');
$objWriter->endElement();
$objWriter->startElement('c:showKeys');
$objWriter->writeAttribute('val', $dataTable->getShowKeys() ? '1' : '0');
$objWriter->endElement();
$objWriter->endElement(); // c:dTable
}
private function writeDataLabelsBool(XMLWriter $objWriter, string $name, ?bool $value): void
{
if ($value !== null) {
@@ -0,0 +1,173 @@
<?php
declare(strict_types=1);
namespace PhpOffice\PhpSpreadsheetTests\Chart;
use PhpOffice\PhpSpreadsheet\Chart\Chart;
use PhpOffice\PhpSpreadsheet\Chart\DataSeries;
use PhpOffice\PhpSpreadsheet\Chart\DataSeriesValues;
use PhpOffice\PhpSpreadsheet\Chart\DataTable;
//use PhpOffice\PhpSpreadsheet\Chart\Legend as ChartLegend;
use PhpOffice\PhpSpreadsheet\Chart\PlotArea;
use PhpOffice\PhpSpreadsheet\Chart\Title;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Theme as SpreadsheetTheme;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
class DataTableTest extends AbstractFunctional
{
public function testSetDataTable(): void
{
$dataTable = new DataTable();
self::assertTrue($dataTable->getShowHorizontalBorder());
self::assertTrue($dataTable->getShowVerticalBorder());
self::assertTrue($dataTable->getShowOutline());
self::assertTrue($dataTable->getShowKeys());
$dataTable->setShowHorizontalBorder(false)
->setShowVerticalBorder(false)
->setShowOutline(false)
->setShowKeys(false);
self::assertFalse($dataTable->getShowHorizontalBorder());
self::assertFalse($dataTable->getShowVerticalBorder());
self::assertFalse($dataTable->getShowOutline());
self::assertFalse($dataTable->getShowKeys());
}
private function readCharts(XlsxReader $reader): void
{
$reader->setIncludeCharts(true);
}
private function writeCharts(XlsxWriter $writer): void
{
$writer->setIncludeCharts(true);
}
public function testCopyDataTable(): void
{
$spreadsheet = new Spreadsheet();
// based on 33_Chart_create_area3.
$spreadsheet->getTheme()
->setThemeColorName(
SpreadsheetTheme::COLOR_SCHEME_2013_2022_NAME
);
$worksheet = $spreadsheet->getActiveSheet();
$sheet2 = $spreadsheet->createSheet();
$sheet2->setTitle('Sheet2');
$worksheet->fromArray(
[
['', 2010, 2011, 2012],
['Q1', 12, 15, 21],
['Q2', 56, 73, 86],
['Q3', 52, 61, 69],
['Q4', 30, 32, 0],
]
);
// Set the Labels for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$dataSeriesLabels = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$B$1', null, 1), // 2010
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$C$1', null, 1), // 2011
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$D$1', null, 1), // 2012
];
// Set the X-Axis Labels
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$xAxisTickValues = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_STRING, 'Worksheet!$A$2:$A$5', null, 4), // Q1 to Q4
];
// Set the Data values for each data series we want to plot
// Datatype
// Cell reference for data
// Format Code
// Number of datapoints in series
// Data values
// Data Marker
$dataSeriesValues = [
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$B$2:$B$5', null, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$C$2:$C$5', null, 4),
new DataSeriesValues(DataSeriesValues::DATASERIES_TYPE_NUMBER, 'Worksheet!$D$2:$D$5', null, 4),
];
// Build the dataseries
$series = new DataSeries(
DataSeries::TYPE_AREACHART, // plotType
DataSeries::GROUPING_PERCENT_STACKED, // plotGrouping
range(0, count($dataSeriesValues) - 1), // plotOrder
$dataSeriesLabels, // plotLabel
$xAxisTickValues, // plotCategory
$dataSeriesValues // plotValues
);
// Set the series in the plot area
$plotArea = new PlotArea(null, [$series]);
$plotArea->setDataTable(new DataTable());
// No need for Legend if using DataTable
$legend = null; //new ChartLegend(ChartLegend::POSITION_TOPRIGHT, null, false);
$title = new Title('Test %age-Stacked Area Chart');
$yAxisLabel = new Title('Value ($k)');
// Create the chart
$chart = new Chart(
'chart1', // name
$title, // title
$legend, // legend
$plotArea, // plotArea
true, // plotVisibleOnly
DataSeries::EMPTY_AS_GAP, // displayBlanksAs
null, // xAxisLabel
$yAxisLabel // yAxisLabel
);
// Set the position where the chart should appear in the worksheet
$chart->setTopLeftPosition('A1');
$chart->setBottomRightPosition('H18');
// Add the chart to the worksheet
$sheet2->addChart($chart);
$spreadsheet->setActiveSheetIndex(1);
// Save Excel 2007 file
/** @var callable */
$callableReader = [$this, 'readCharts'];
/** @var callable */
$callableWriter = [$this, 'writeCharts'];
$reloadedSpreadsheet = $this->writeAndReload(
$spreadsheet,
'Xlsx',
$this->readCharts(...),
$this->writeCharts(...)
);
$spreadsheet->disconnectWorksheets();
$sheet = $reloadedSpreadsheet->getActiveSheet();
$charts2 = $sheet->getChartCollection();
self::assertCount(1, $charts2);
$chart2 = $charts2[0];
self::assertNotNull($chart2);
$plotArea = $chart2->getPlotArea();
self::assertNotNull($plotArea);
$dtab = $plotArea->getDataTable();
self::assertNotNull($dtab);
self::assertTrue($dtab->getShowHorizontalBorder());
self::assertTrue($dtab->getShowVerticalBorder());
self::assertTrue($dtab->getShowOutline());
self::assertTrue($dtab->getShowKeys());
$reloadedSpreadsheet->disconnectWorksheets();
}
}