Format as Table for Xlsx
Initial implementation of Excel's tables feature (i.e. Select Home >
Format as Table in Excel App).
Tables are similar to AutoFilter but tables have other advantages like
named ranges, easy formatting, totals row and header row with filter.
Tables can also be converted to charts and pivot tables easily.
Usage:
$table = new Table();
$table->setName('Sales_Data');
$table->setRange('A1:D17');
$spreadsheet->getActiveSheet()->addTable($table);
In this Commit:
- Added Table API with initial support for header and totals row.
- Added complete styling options for Table.
- Added Xlsx Writer for Table.
- Added samples.
- Covered with unit tests.
To be done:
- Filter expressions similar to AutoFilter.
- Precalucate formulas for totals row (Check sample 2).
- Table named ranges in formulas and calculation.
This commit is contained in:
parent
1801f582f5
commit
92a50d134f
|
|
@ -5527,7 +5527,7 @@ parameters:
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#2 \\$id of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Rels\\:\\:writeRelationship\\(\\) expects int, string given\\.$#"
|
message: "#^Parameter \\#2 \\$id of method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Rels\\:\\:writeRelationship\\(\\) expects int, string given\\.$#"
|
||||||
count: 4
|
count: 5
|
||||||
path: src/PhpSpreadsheet/Writer/Xlsx/Rels.php
|
path: src/PhpSpreadsheet/Writer/Xlsx/Rels.php
|
||||||
|
|
||||||
-
|
-
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Table\TableStyle;
|
||||||
|
|
||||||
|
require __DIR__ . '/../Header.php';
|
||||||
|
|
||||||
|
// Create new Spreadsheet object
|
||||||
|
$helper->log('Create new Spreadsheet object');
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
|
||||||
|
// Set document properties
|
||||||
|
$helper->log('Set document properties');
|
||||||
|
$spreadsheet->getProperties()->setCreator('aswinkumar863')
|
||||||
|
->setLastModifiedBy('aswinkumar863')
|
||||||
|
->setTitle('PhpSpreadsheet Table Test Document')
|
||||||
|
->setSubject('PhpSpreadsheet Table Test Document')
|
||||||
|
->setDescription('Test document for PhpSpreadsheet, generated using PHP classes.')
|
||||||
|
->setKeywords('office PhpSpreadsheet php')
|
||||||
|
->setCategory('Table');
|
||||||
|
|
||||||
|
// Create the worksheet
|
||||||
|
$helper->log('Add data');
|
||||||
|
|
||||||
|
$spreadsheet->setActiveSheetIndex(0);
|
||||||
|
$spreadsheet->getActiveSheet()->setCellValue('A1', 'Year')
|
||||||
|
->setCellValue('B1', 'Quarter')
|
||||||
|
->setCellValue('C1', 'Country')
|
||||||
|
->setCellValue('D1', 'Sales');
|
||||||
|
|
||||||
|
$dataArray = [
|
||||||
|
['2010', 'Q1', 'United States', 790],
|
||||||
|
['2010', 'Q2', 'United States', 730],
|
||||||
|
['2010', 'Q3', 'United States', 860],
|
||||||
|
['2010', 'Q4', 'United States', 850],
|
||||||
|
['2011', 'Q1', 'United States', 800],
|
||||||
|
['2011', 'Q2', 'United States', 700],
|
||||||
|
['2011', 'Q3', 'United States', 900],
|
||||||
|
['2011', 'Q4', 'United States', 950],
|
||||||
|
['2010', 'Q1', 'Belgium', 380],
|
||||||
|
['2010', 'Q2', 'Belgium', 390],
|
||||||
|
['2010', 'Q3', 'Belgium', 420],
|
||||||
|
['2010', 'Q4', 'Belgium', 460],
|
||||||
|
['2011', 'Q1', 'Belgium', 400],
|
||||||
|
['2011', 'Q2', 'Belgium', 350],
|
||||||
|
['2011', 'Q3', 'Belgium', 450],
|
||||||
|
['2011', 'Q4', 'Belgium', 500],
|
||||||
|
];
|
||||||
|
|
||||||
|
$spreadsheet->getActiveSheet()->fromArray($dataArray, null, 'A2');
|
||||||
|
|
||||||
|
// Create Table
|
||||||
|
$helper->log('Create Table');
|
||||||
|
$table = new Table();
|
||||||
|
$table->setName('Sales Data');
|
||||||
|
$table->setRange('A1:D17');
|
||||||
|
|
||||||
|
// Create Columns
|
||||||
|
$table->getColumn('D')->setShowFilterButton(false);
|
||||||
|
|
||||||
|
// Create Table Style
|
||||||
|
$helper->log('Create Table Style');
|
||||||
|
$tableStyle = new TableStyle();
|
||||||
|
$tableStyle->setTheme(TableStyle::TABLE_STYLE_MEDIUM2);
|
||||||
|
$tableStyle->setShowRowStripes(true);
|
||||||
|
$tableStyle->setShowColumnStripes(true);
|
||||||
|
$tableStyle->setShowFirstColumn(true);
|
||||||
|
$tableStyle->setShowLastColumn(true);
|
||||||
|
$table->setStyle($tableStyle);
|
||||||
|
|
||||||
|
// Add Table to Worksheet
|
||||||
|
$helper->log('Add Table to Worksheet');
|
||||||
|
$spreadsheet->getActiveSheet()->addTable($table);
|
||||||
|
|
||||||
|
// Save
|
||||||
|
$helper->write($spreadsheet, __FILE__, ['Xlsx']);
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
|
||||||
|
|
||||||
|
require __DIR__ . '/../Header.php';
|
||||||
|
|
||||||
|
// Create new Spreadsheet object
|
||||||
|
$helper->log('Create new Spreadsheet object');
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
|
||||||
|
// Set document properties
|
||||||
|
$helper->log('Set document properties');
|
||||||
|
$spreadsheet->getProperties()->setCreator('aswinkumar863')
|
||||||
|
->setLastModifiedBy('aswinkumar863')
|
||||||
|
->setTitle('PhpSpreadsheet Table Test Document')
|
||||||
|
->setSubject('PhpSpreadsheet Table Test Document')
|
||||||
|
->setDescription('Test document for PhpSpreadsheet, generated using PHP classes.')
|
||||||
|
->setKeywords('office PhpSpreadsheet php')
|
||||||
|
->setCategory('Table');
|
||||||
|
|
||||||
|
// Create the worksheet
|
||||||
|
$helper->log('Add data');
|
||||||
|
|
||||||
|
$spreadsheet->setActiveSheetIndex(0);
|
||||||
|
$spreadsheet->getActiveSheet()->setCellValue('A1', 'Year')
|
||||||
|
->setCellValue('B1', 'Quarter')
|
||||||
|
->setCellValue('C1', 'Country')
|
||||||
|
->setCellValue('D1', 'Sales');
|
||||||
|
|
||||||
|
$dataArray = [
|
||||||
|
['2010', 'Q1', 'United States', 790],
|
||||||
|
['2010', 'Q2', 'United States', 730],
|
||||||
|
['2010', 'Q3', 'United States', 860],
|
||||||
|
['2010', 'Q4', 'United States', 850],
|
||||||
|
['2011', 'Q1', 'United States', 800],
|
||||||
|
['2011', 'Q2', 'United States', 700],
|
||||||
|
['2011', 'Q3', 'United States', 900],
|
||||||
|
['2011', 'Q4', 'United States', 950],
|
||||||
|
['2010', 'Q1', 'Belgium', 380],
|
||||||
|
['2010', 'Q2', 'Belgium', 390],
|
||||||
|
['2010', 'Q3', 'Belgium', 420],
|
||||||
|
['2010', 'Q4', 'Belgium', 460],
|
||||||
|
['2011', 'Q1', 'Belgium', 400],
|
||||||
|
['2011', 'Q2', 'Belgium', 350],
|
||||||
|
['2011', 'Q3', 'Belgium', 450],
|
||||||
|
['2011', 'Q4', 'Belgium', 500],
|
||||||
|
];
|
||||||
|
|
||||||
|
$spreadsheet->getActiveSheet()->fromArray($dataArray, null, 'A2');
|
||||||
|
|
||||||
|
// Table
|
||||||
|
$helper->log('Create Table');
|
||||||
|
$table = new Table();
|
||||||
|
$table->setName('SalesData');
|
||||||
|
$table->setShowTotalsRow(true);
|
||||||
|
$table->setRange('A1:D18'); // +1 row for totalsRow
|
||||||
|
|
||||||
|
$helper->log('Add Totals Row');
|
||||||
|
// Table column label not implemented yet,
|
||||||
|
$table->getColumn('A')->setTotalsRowLabel('Total');
|
||||||
|
// So set the label directly to the cell
|
||||||
|
$spreadsheet->getActiveSheet()->getCell('A18')->setValue('Total');
|
||||||
|
|
||||||
|
// Table column function not implemented yet,
|
||||||
|
$table->getColumn('D')->setTotalsRowFunction('sum');
|
||||||
|
// So set the formula directly to the cell
|
||||||
|
$spreadsheet->getActiveSheet()->getCell('D18')->setValue('=SUBTOTAL(109,SalesData[Sales])');
|
||||||
|
|
||||||
|
// Add Table to Worksheet
|
||||||
|
$helper->log('Add Table to Worksheet');
|
||||||
|
$spreadsheet->getActiveSheet()->addTable($table);
|
||||||
|
|
||||||
|
// Save
|
||||||
|
$path = $helper->getFilename(__FILE__);
|
||||||
|
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
|
||||||
|
|
||||||
|
// Disable precalculation to add table's total row
|
||||||
|
$writer->setPreCalculateFormulas(false);
|
||||||
|
$callStartTime = microtime(true);
|
||||||
|
$writer->save($path);
|
||||||
|
$helper->logWrite($writer, $path, $callStartTime);
|
||||||
|
$helper->logEndingNotes();
|
||||||
|
|
@ -0,0 +1,407 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheet\Worksheet;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Table\TableStyle;
|
||||||
|
|
||||||
|
class Table
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Table Name.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $name = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show Header Row.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $showHeaderRow = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show Totals Row.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $showTotalsRow = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table Range.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $range = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table Worksheet.
|
||||||
|
*
|
||||||
|
* @var null|Worksheet
|
||||||
|
*/
|
||||||
|
private $workSheet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table Column.
|
||||||
|
*
|
||||||
|
* @var Table\Column[]
|
||||||
|
*/
|
||||||
|
private $columns = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table Style.
|
||||||
|
*
|
||||||
|
* @var TableStyle
|
||||||
|
*/
|
||||||
|
private $style;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Table.
|
||||||
|
*
|
||||||
|
* @param string $range (e.g. A1:D4)
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function __construct(string $range = '', ?Worksheet $worksheet = null)
|
||||||
|
{
|
||||||
|
$this->setRange($range);
|
||||||
|
$this->setWorksheet($worksheet);
|
||||||
|
$this->style = new TableStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Table name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Table name.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setName(string $name)
|
||||||
|
{
|
||||||
|
$this->name = preg_replace('/\s+/', '_', trim($name)) ?? '';
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get show Header Row.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getShowHeaderRow()
|
||||||
|
{
|
||||||
|
return $this->showHeaderRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set show Header Row.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setShowHeaderRow(bool $showHeaderRow)
|
||||||
|
{
|
||||||
|
$this->showHeaderRow = $showHeaderRow;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get show Totals Row.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getShowTotalsRow()
|
||||||
|
{
|
||||||
|
return $this->showTotalsRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set show Totals Row.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setShowTotalsRow(bool $showTotalsRow)
|
||||||
|
{
|
||||||
|
$this->showTotalsRow = $showTotalsRow;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Table Range.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRange()
|
||||||
|
{
|
||||||
|
return $this->range;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Table Cell Range.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setRange(string $range): self
|
||||||
|
{
|
||||||
|
// extract coordinate
|
||||||
|
[$worksheet, $range] = Worksheet::extractSheetTitle($range, true);
|
||||||
|
if (empty($range)) {
|
||||||
|
// Discard all column rules
|
||||||
|
$this->columns = [];
|
||||||
|
$this->range = '';
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($range, ':') === false) {
|
||||||
|
throw new PhpSpreadsheetException('Table must be set on a range of cells.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->range = $range;
|
||||||
|
// Discard any column ruless that are no longer valid within this range
|
||||||
|
[$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
|
||||||
|
foreach ($this->columns as $key => $value) {
|
||||||
|
$colIndex = Coordinate::columnIndexFromString($key);
|
||||||
|
if (($rangeStart[0] > $colIndex) || ($rangeEnd[0] < $colIndex)) {
|
||||||
|
unset($this->columns[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Table Cell Range to max row.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setRangeToMaxRow(): self
|
||||||
|
{
|
||||||
|
if ($this->workSheet !== null) {
|
||||||
|
$thisrange = $this->range;
|
||||||
|
$range = preg_replace('/\\d+$/', (string) $this->workSheet->getHighestRow(), $thisrange) ?? '';
|
||||||
|
if ($range !== $thisrange) {
|
||||||
|
$this->setRange($range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Table's Worksheet.
|
||||||
|
*
|
||||||
|
* @return null|Worksheet
|
||||||
|
*/
|
||||||
|
public function getWorksheet()
|
||||||
|
{
|
||||||
|
return $this->workSheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Table's Worksheet.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setWorksheet(?Worksheet $worksheet = null)
|
||||||
|
{
|
||||||
|
$this->workSheet = $worksheet;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all Table Columns.
|
||||||
|
*
|
||||||
|
* @return Table\Column[]
|
||||||
|
*/
|
||||||
|
public function getColumns()
|
||||||
|
{
|
||||||
|
return $this->columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate that the specified column is in the Table range.
|
||||||
|
*
|
||||||
|
* @param string $column Column name (e.g. A)
|
||||||
|
*
|
||||||
|
* @return int The column offset within the table range
|
||||||
|
*/
|
||||||
|
public function testColumnInRange($column)
|
||||||
|
{
|
||||||
|
if (empty($this->range)) {
|
||||||
|
throw new PhpSpreadsheetException('No table range is defined.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$columnIndex = Coordinate::columnIndexFromString($column);
|
||||||
|
[$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
|
||||||
|
if (($rangeStart[0] > $columnIndex) || ($rangeEnd[0] < $columnIndex)) {
|
||||||
|
throw new PhpSpreadsheetException('Column is outside of current table range.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $columnIndex - $rangeStart[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a specified Table Column Offset within the defined Table range.
|
||||||
|
*
|
||||||
|
* @param string $column Column name (e.g. A)
|
||||||
|
*
|
||||||
|
* @return int The offset of the specified column within the table range
|
||||||
|
*/
|
||||||
|
public function getColumnOffset($column)
|
||||||
|
{
|
||||||
|
return $this->testColumnInRange($column);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a specified Table Column.
|
||||||
|
*
|
||||||
|
* @param string $column Column name (e.g. A)
|
||||||
|
*
|
||||||
|
* @return Table\Column
|
||||||
|
*/
|
||||||
|
public function getColumn($column)
|
||||||
|
{
|
||||||
|
$this->testColumnInRange($column);
|
||||||
|
|
||||||
|
if (!isset($this->columns[$column])) {
|
||||||
|
$this->columns[$column] = new Table\Column($column, $this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->columns[$column];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a specified Table Column by it's offset.
|
||||||
|
*
|
||||||
|
* @param int $columnOffset Column offset within range (starting from 0)
|
||||||
|
*
|
||||||
|
* @return Table\Column
|
||||||
|
*/
|
||||||
|
public function getColumnByOffset($columnOffset)
|
||||||
|
{
|
||||||
|
[$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($this->range);
|
||||||
|
$pColumn = Coordinate::stringFromColumnIndex($rangeStart[0] + $columnOffset);
|
||||||
|
|
||||||
|
return $this->getColumn($pColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Table.
|
||||||
|
*
|
||||||
|
* @param string|Table\Column $columnObjectOrString
|
||||||
|
* A simple string containing a Column ID like 'A' is permitted
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setColumn($columnObjectOrString)
|
||||||
|
{
|
||||||
|
if ((is_string($columnObjectOrString)) && (!empty($columnObjectOrString))) {
|
||||||
|
$column = $columnObjectOrString;
|
||||||
|
} elseif (is_object($columnObjectOrString) && ($columnObjectOrString instanceof Table\Column)) {
|
||||||
|
$column = $columnObjectOrString->getColumnIndex();
|
||||||
|
} else {
|
||||||
|
throw new PhpSpreadsheetException('Column is not within the table range.');
|
||||||
|
}
|
||||||
|
$this->testColumnInRange($column);
|
||||||
|
|
||||||
|
if (is_string($columnObjectOrString)) {
|
||||||
|
$this->columns[$columnObjectOrString] = new Table\Column($columnObjectOrString, $this);
|
||||||
|
} else {
|
||||||
|
$columnObjectOrString->setTable($this);
|
||||||
|
$this->columns[$column] = $columnObjectOrString;
|
||||||
|
}
|
||||||
|
ksort($this->columns);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear a specified Table Column.
|
||||||
|
*
|
||||||
|
* @param string $column Column name (e.g. A)
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function clearColumn($column)
|
||||||
|
{
|
||||||
|
$this->testColumnInRange($column);
|
||||||
|
|
||||||
|
if (isset($this->columns[$column])) {
|
||||||
|
unset($this->columns[$column]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get table Style.
|
||||||
|
*
|
||||||
|
* @return TableStyle
|
||||||
|
*/
|
||||||
|
public function getStyle()
|
||||||
|
{
|
||||||
|
return $this->style;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set table Style.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setStyle(TableStyle $style)
|
||||||
|
{
|
||||||
|
$this->style = $style;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement PHP __clone to create a deep clone, not just a shallow copy.
|
||||||
|
*/
|
||||||
|
public function __clone()
|
||||||
|
{
|
||||||
|
$vars = get_object_vars($this);
|
||||||
|
foreach ($vars as $key => $value) {
|
||||||
|
if (is_object($value)) {
|
||||||
|
if ($key === 'workSheet') {
|
||||||
|
// Detach from worksheet
|
||||||
|
$this->{$key} = null;
|
||||||
|
} else {
|
||||||
|
$this->{$key} = clone $value;
|
||||||
|
}
|
||||||
|
} elseif ((is_array($value)) && ($key == 'columns')) {
|
||||||
|
// The columns array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table objects
|
||||||
|
$this->{$key} = [];
|
||||||
|
foreach ($value as $k => $v) {
|
||||||
|
$this->{$key}[$k] = clone $v;
|
||||||
|
// attach the new cloned Column to this new cloned Table object
|
||||||
|
$this->{$key}[$k]->setTable($this);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->{$key} = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* toString method replicates previous behavior by returning the range if object is
|
||||||
|
* referenced as a property of its worksheet.
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return (string) $this->range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheet\Worksheet\Table;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
|
||||||
|
|
||||||
|
class Column
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Table Column Index.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $columnIndex = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show Filter Button.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $showFilterButton = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total Row Label.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $totalsRowLabel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total Row Function.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $totalsRowFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total Row Formula.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $totalsRowFormula;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table.
|
||||||
|
*
|
||||||
|
* @var null|Table
|
||||||
|
*/
|
||||||
|
private $table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Column.
|
||||||
|
*
|
||||||
|
* @param string $column Column (e.g. A)
|
||||||
|
* @param Table $table Table for this column
|
||||||
|
*/
|
||||||
|
public function __construct($column, ?Table $table = null)
|
||||||
|
{
|
||||||
|
$this->columnIndex = $column;
|
||||||
|
$this->table = $table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Table column index as string eg: 'A'.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getColumnIndex()
|
||||||
|
{
|
||||||
|
return $this->columnIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Table column index as string eg: 'A'.
|
||||||
|
*
|
||||||
|
* @param string $column Column (e.g. A)
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setColumnIndex($column)
|
||||||
|
{
|
||||||
|
// Uppercase coordinate
|
||||||
|
$column = strtoupper($column);
|
||||||
|
if ($this->table !== null) {
|
||||||
|
$this->table->testColumnInRange($column);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->columnIndex = $column;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get show Filter Button.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getShowFilterButton()
|
||||||
|
{
|
||||||
|
return $this->showFilterButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set show Filter Button.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setShowFilterButton(bool $showFilterButton)
|
||||||
|
{
|
||||||
|
$this->showFilterButton = $showFilterButton;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get total Row Label.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTotalsRowLabel()
|
||||||
|
{
|
||||||
|
return $this->totalsRowLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set total Row Label.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setTotalsRowLabel(string $totalsRowLabel)
|
||||||
|
{
|
||||||
|
$this->totalsRowLabel = $totalsRowLabel;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get total Row Function.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTotalsRowFunction()
|
||||||
|
{
|
||||||
|
return $this->totalsRowFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set total Row Function.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setTotalsRowFunction(string $totalsRowFunction)
|
||||||
|
{
|
||||||
|
$this->totalsRowFunction = $totalsRowFunction;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get total Row Formula.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTotalsRowFormula()
|
||||||
|
{
|
||||||
|
return $this->totalsRowFormula;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set total Row Formula.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setTotalsRowFormula(string $totalsRowFormula)
|
||||||
|
{
|
||||||
|
$this->totalsRowFormula = $totalsRowFormula;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get this Column's Table.
|
||||||
|
*
|
||||||
|
* @return null|Table
|
||||||
|
*/
|
||||||
|
public function getTable()
|
||||||
|
{
|
||||||
|
return $this->table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this Column's Table.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setTable(?Table $table = null)
|
||||||
|
{
|
||||||
|
$this->table = $table;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,254 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheet\Worksheet\Table;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
|
||||||
|
|
||||||
|
class TableStyle
|
||||||
|
{
|
||||||
|
const TABLE_STYLE_NONE = '';
|
||||||
|
const TABLE_STYLE_LIGHT1 = 'TableStyleLight1';
|
||||||
|
const TABLE_STYLE_LIGHT2 = 'TableStyleLight2';
|
||||||
|
const TABLE_STYLE_LIGHT3 = 'TableStyleLight3';
|
||||||
|
const TABLE_STYLE_LIGHT4 = 'TableStyleLight4';
|
||||||
|
const TABLE_STYLE_LIGHT5 = 'TableStyleLight5';
|
||||||
|
const TABLE_STYLE_LIGHT6 = 'TableStyleLight6';
|
||||||
|
const TABLE_STYLE_LIGHT7 = 'TableStyleLight7';
|
||||||
|
const TABLE_STYLE_LIGHT8 = 'TableStyleLight8';
|
||||||
|
const TABLE_STYLE_LIGHT9 = 'TableStyleLight9';
|
||||||
|
const TABLE_STYLE_LIGHT10 = 'TableStyleLight10';
|
||||||
|
const TABLE_STYLE_LIGHT11 = 'TableStyleLight11';
|
||||||
|
const TABLE_STYLE_LIGHT12 = 'TableStyleLight12';
|
||||||
|
const TABLE_STYLE_LIGHT13 = 'TableStyleLight13';
|
||||||
|
const TABLE_STYLE_LIGHT14 = 'TableStyleLight14';
|
||||||
|
const TABLE_STYLE_LIGHT15 = 'TableStyleLight15';
|
||||||
|
const TABLE_STYLE_LIGHT16 = 'TableStyleLight16';
|
||||||
|
const TABLE_STYLE_LIGHT17 = 'TableStyleLight17';
|
||||||
|
const TABLE_STYLE_LIGHT18 = 'TableStyleLight18';
|
||||||
|
const TABLE_STYLE_LIGHT19 = 'TableStyleLight19';
|
||||||
|
const TABLE_STYLE_LIGHT20 = 'TableStyleLight20';
|
||||||
|
const TABLE_STYLE_LIGHT21 = 'TableStyleLight21';
|
||||||
|
const TABLE_STYLE_MEDIUM1 = 'TableStyleMedium1';
|
||||||
|
const TABLE_STYLE_MEDIUM2 = 'TableStyleMedium2';
|
||||||
|
const TABLE_STYLE_MEDIUM3 = 'TableStyleMedium3';
|
||||||
|
const TABLE_STYLE_MEDIUM4 = 'TableStyleMedium4';
|
||||||
|
const TABLE_STYLE_MEDIUM5 = 'TableStyleMedium5';
|
||||||
|
const TABLE_STYLE_MEDIUM6 = 'TableStyleMedium6';
|
||||||
|
const TABLE_STYLE_MEDIUM7 = 'TableStyleMedium7';
|
||||||
|
const TABLE_STYLE_MEDIUM8 = 'TableStyleMedium8';
|
||||||
|
const TABLE_STYLE_MEDIUM9 = 'TableStyleMedium9';
|
||||||
|
const TABLE_STYLE_MEDIUM10 = 'TableStyleMedium10';
|
||||||
|
const TABLE_STYLE_MEDIUM11 = 'TableStyleMedium11';
|
||||||
|
const TABLE_STYLE_MEDIUM12 = 'TableStyleMedium12';
|
||||||
|
const TABLE_STYLE_MEDIUM13 = 'TableStyleMedium13';
|
||||||
|
const TABLE_STYLE_MEDIUM14 = 'TableStyleMedium14';
|
||||||
|
const TABLE_STYLE_MEDIUM15 = 'TableStyleMedium15';
|
||||||
|
const TABLE_STYLE_MEDIUM16 = 'TableStyleMedium16';
|
||||||
|
const TABLE_STYLE_MEDIUM17 = 'TableStyleMedium17';
|
||||||
|
const TABLE_STYLE_MEDIUM18 = 'TableStyleMedium18';
|
||||||
|
const TABLE_STYLE_MEDIUM19 = 'TableStyleMedium19';
|
||||||
|
const TABLE_STYLE_MEDIUM20 = 'TableStyleMedium20';
|
||||||
|
const TABLE_STYLE_MEDIUM21 = 'TableStyleMedium21';
|
||||||
|
const TABLE_STYLE_MEDIUM22 = 'TableStyleMedium22';
|
||||||
|
const TABLE_STYLE_MEDIUM23 = 'TableStyleMedium23';
|
||||||
|
const TABLE_STYLE_MEDIUM24 = 'TableStyleMedium24';
|
||||||
|
const TABLE_STYLE_MEDIUM25 = 'TableStyleMedium25';
|
||||||
|
const TABLE_STYLE_MEDIUM26 = 'TableStyleMedium26';
|
||||||
|
const TABLE_STYLE_MEDIUM27 = 'TableStyleMedium27';
|
||||||
|
const TABLE_STYLE_MEDIUM28 = 'TableStyleMedium28';
|
||||||
|
const TABLE_STYLE_DARK1 = 'TableStyleDark1';
|
||||||
|
const TABLE_STYLE_DARK2 = 'TableStyleDark2';
|
||||||
|
const TABLE_STYLE_DARK3 = 'TableStyleDark3';
|
||||||
|
const TABLE_STYLE_DARK4 = 'TableStyleDark4';
|
||||||
|
const TABLE_STYLE_DARK5 = 'TableStyleDark5';
|
||||||
|
const TABLE_STYLE_DARK6 = 'TableStyleDark6';
|
||||||
|
const TABLE_STYLE_DARK7 = 'TableStyleDark7';
|
||||||
|
const TABLE_STYLE_DARK8 = 'TableStyleDark8';
|
||||||
|
const TABLE_STYLE_DARK9 = 'TableStyleDark9';
|
||||||
|
const TABLE_STYLE_DARK10 = 'TableStyleDark10';
|
||||||
|
const TABLE_STYLE_DARK11 = 'TableStyleDark11';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Theme.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $theme;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show First Column.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $showFirstColumn = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show Last Column.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $showLastColumn = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show Row Stripes.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $showRowStripes = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show Column Stripes.
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $showColumnStripes = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table.
|
||||||
|
*
|
||||||
|
* @var null|Table
|
||||||
|
*/
|
||||||
|
private $table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Table Style.
|
||||||
|
*
|
||||||
|
* @param string $theme (e.g. TableStyle::TABLE_STYLE_MEDIUM2)
|
||||||
|
*/
|
||||||
|
public function __construct(string $theme = self::TABLE_STYLE_MEDIUM2)
|
||||||
|
{
|
||||||
|
$this->theme = $theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get theme.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTheme()
|
||||||
|
{
|
||||||
|
return $this->theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set theme.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setTheme(string $theme)
|
||||||
|
{
|
||||||
|
$this->theme = $theme;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get show First Column.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getShowFirstColumn()
|
||||||
|
{
|
||||||
|
return $this->showFirstColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set show First Column.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setShowFirstColumn(bool $showFirstColumn)
|
||||||
|
{
|
||||||
|
$this->showFirstColumn = $showFirstColumn;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get show Last Column.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getShowLastColumn()
|
||||||
|
{
|
||||||
|
return $this->showLastColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set show Last Column.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setShowLastColumn(bool $showLastColumn)
|
||||||
|
{
|
||||||
|
$this->showLastColumn = $showLastColumn;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get show Row Stripes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getShowRowStripes()
|
||||||
|
{
|
||||||
|
return $this->showRowStripes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set show Row Stripes.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setShowRowStripes(bool $showRowStripes)
|
||||||
|
{
|
||||||
|
$this->showRowStripes = $showRowStripes;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get show Column Stripes.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getShowColumnStripes()
|
||||||
|
{
|
||||||
|
return $this->showColumnStripes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set show Column Stripes.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setShowColumnStripes(bool $showColumnStripes)
|
||||||
|
{
|
||||||
|
$this->showColumnStripes = $showColumnStripes;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get this Style's Table.
|
||||||
|
*
|
||||||
|
* @return null|Table
|
||||||
|
*/
|
||||||
|
public function getTable()
|
||||||
|
{
|
||||||
|
return $this->table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this Style's Table.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setTable(?Table $table = null)
|
||||||
|
{
|
||||||
|
$this->table = $table;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -107,6 +107,13 @@ class Worksheet implements IComparable
|
||||||
*/
|
*/
|
||||||
private $chartCollection;
|
private $chartCollection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of Table objects.
|
||||||
|
*
|
||||||
|
* @var ArrayObject<int, Table>
|
||||||
|
*/
|
||||||
|
private $tableCollection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Worksheet title.
|
* Worksheet title.
|
||||||
*
|
*
|
||||||
|
|
@ -371,7 +378,10 @@ class Worksheet implements IComparable
|
||||||
$this->defaultRowDimension = new RowDimension(null);
|
$this->defaultRowDimension = new RowDimension(null);
|
||||||
// Default column dimension
|
// Default column dimension
|
||||||
$this->defaultColumnDimension = new ColumnDimension(null);
|
$this->defaultColumnDimension = new ColumnDimension(null);
|
||||||
|
// AutoFilter
|
||||||
$this->autoFilter = new AutoFilter('', $this);
|
$this->autoFilter = new AutoFilter('', $this);
|
||||||
|
// Table collection
|
||||||
|
$this->tableCollection = new ArrayObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -2005,6 +2015,56 @@ class Worksheet implements IComparable
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get collection of Tables.
|
||||||
|
*
|
||||||
|
* @return ArrayObject<int, Table>
|
||||||
|
*/
|
||||||
|
public function getTableCollection()
|
||||||
|
{
|
||||||
|
return $this->tableCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Table.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addTable(Table $table): self
|
||||||
|
{
|
||||||
|
$table->setWorksheet($this);
|
||||||
|
$this->tableCollection[] = $table;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Table Range by using numeric cell coordinates.
|
||||||
|
*
|
||||||
|
* @param int $columnIndex1 Numeric column coordinate of the first cell
|
||||||
|
* @param int $row1 Numeric row coordinate of the first cell
|
||||||
|
* @param int $columnIndex2 Numeric column coordinate of the second cell
|
||||||
|
* @param int $row2 Numeric row coordinate of the second cell
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addTableByColumnAndRow($columnIndex1, $row1, $columnIndex2, $row2): self
|
||||||
|
{
|
||||||
|
$cellRange = Coordinate::stringFromColumnIndex($columnIndex1) . $row1 . ':' . Coordinate::stringFromColumnIndex($columnIndex2) . $row2;
|
||||||
|
|
||||||
|
return $this->addTable(new Table($cellRange, $this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove collection of Tables.
|
||||||
|
*/
|
||||||
|
public function removeTableCollection(): self
|
||||||
|
{
|
||||||
|
$this->tableCollection = new ArrayObject();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Freeze Pane.
|
* Get Freeze Pane.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsRibbon;
|
||||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsVBA;
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\RelsVBA;
|
||||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\StringTable;
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\StringTable;
|
||||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Style;
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Style;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Table;
|
||||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Theme;
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Theme;
|
||||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Workbook;
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Workbook;
|
||||||
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet;
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet;
|
||||||
|
|
@ -167,6 +168,11 @@ class Xlsx extends BaseWriter
|
||||||
*/
|
*/
|
||||||
private $writerPartTheme;
|
private $writerPartTheme;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Table
|
||||||
|
*/
|
||||||
|
private $writerPartTable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Workbook
|
* @var Workbook
|
||||||
*/
|
*/
|
||||||
|
|
@ -196,6 +202,7 @@ class Xlsx extends BaseWriter
|
||||||
$this->writerPartStringTable = new StringTable($this);
|
$this->writerPartStringTable = new StringTable($this);
|
||||||
$this->writerPartStyle = new Style($this);
|
$this->writerPartStyle = new Style($this);
|
||||||
$this->writerPartTheme = new Theme($this);
|
$this->writerPartTheme = new Theme($this);
|
||||||
|
$this->writerPartTable = new Table($this);
|
||||||
$this->writerPartWorkbook = new Workbook($this);
|
$this->writerPartWorkbook = new Workbook($this);
|
||||||
$this->writerPartWorksheet = new Worksheet($this);
|
$this->writerPartWorksheet = new Worksheet($this);
|
||||||
|
|
||||||
|
|
@ -271,6 +278,11 @@ class Xlsx extends BaseWriter
|
||||||
return $this->writerPartTheme;
|
return $this->writerPartTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getWriterPartTable(): Table
|
||||||
|
{
|
||||||
|
return $this->writerPartTable;
|
||||||
|
}
|
||||||
|
|
||||||
public function getWriterPartWorkbook(): Workbook
|
public function getWriterPartWorkbook(): Workbook
|
||||||
{
|
{
|
||||||
return $this->writerPartWorkbook;
|
return $this->writerPartWorkbook;
|
||||||
|
|
@ -389,10 +401,11 @@ class Xlsx extends BaseWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
$chartRef1 = 0;
|
$chartRef1 = 0;
|
||||||
|
$tableRef1 = 1;
|
||||||
// Add worksheet relationships (drawings, ...)
|
// Add worksheet relationships (drawings, ...)
|
||||||
for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
|
for ($i = 0; $i < $this->spreadSheet->getSheetCount(); ++$i) {
|
||||||
// Add relationships
|
// Add relationships
|
||||||
$zipContent['xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels'] = $this->getWriterPartRels()->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts);
|
$zipContent['xl/worksheets/_rels/sheet' . ($i + 1) . '.xml.rels'] = $this->getWriterPartRels()->writeWorksheetRelationships($this->spreadSheet->getSheet($i), ($i + 1), $this->includeCharts, $tableRef1);
|
||||||
|
|
||||||
// Add unparsedLoadedData
|
// Add unparsedLoadedData
|
||||||
$sheetCodeName = $this->spreadSheet->getSheet($i)->getCodeName();
|
$sheetCodeName = $this->spreadSheet->getSheet($i)->getCodeName();
|
||||||
|
|
@ -478,6 +491,12 @@ class Xlsx extends BaseWriter
|
||||||
$zipContent['xl/media/' . $image->getIndexedFilename()] = file_get_contents($image->getPath());
|
$zipContent['xl/media/' . $image->getIndexedFilename()] = file_get_contents($image->getPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add Table parts
|
||||||
|
$tables = $this->spreadSheet->getSheet($i)->getTableCollection();
|
||||||
|
foreach ($tables as $table) {
|
||||||
|
$zipContent['xl/tables/table' . $tableRef1 . '.xml'] = $this->getWriterPartTable()->writeTable($table, $tableRef1++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add media
|
// Add media
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,16 @@ class ContentTypes extends WriterPart
|
||||||
// Shared strings
|
// Shared strings
|
||||||
$this->writeOverrideContentType($objWriter, '/xl/sharedStrings.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml');
|
$this->writeOverrideContentType($objWriter, '/xl/sharedStrings.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml');
|
||||||
|
|
||||||
|
// Table
|
||||||
|
$table = 1;
|
||||||
|
for ($i = 0; $i < $sheetCount; ++$i) {
|
||||||
|
$tableCount = $spreadsheet->getSheet($i)->getTableCollection()->count();
|
||||||
|
|
||||||
|
for ($t = 1; $t <= $tableCount; ++$t) {
|
||||||
|
$this->writeOverrideContentType($objWriter, '/xl/tables/table' . $table++ . '.xml', 'application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add worksheet relationship content types
|
// Add worksheet relationship content types
|
||||||
$unparsedLoadedData = $spreadsheet->getUnparsedLoadedData();
|
$unparsedLoadedData = $spreadsheet->getUnparsedLoadedData();
|
||||||
$chart = 1;
|
$chart = 1;
|
||||||
|
|
|
||||||
|
|
@ -163,10 +163,11 @@ class Rels extends WriterPart
|
||||||
*
|
*
|
||||||
* @param int $worksheetId
|
* @param int $worksheetId
|
||||||
* @param bool $includeCharts Flag indicating if we should write charts
|
* @param bool $includeCharts Flag indicating if we should write charts
|
||||||
|
* @param int $tableRef Table ID
|
||||||
*
|
*
|
||||||
* @return string XML Output
|
* @return string XML Output
|
||||||
*/
|
*/
|
||||||
public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, $worksheetId = 1, $includeCharts = false)
|
public function writeWorksheetRelationships(\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet, $worksheetId = 1, $includeCharts = false, $tableRef = 1)
|
||||||
{
|
{
|
||||||
// Create XML writer
|
// Create XML writer
|
||||||
$objWriter = null;
|
$objWriter = null;
|
||||||
|
|
@ -252,6 +253,17 @@ class Rels extends WriterPart
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write Table
|
||||||
|
$tableCount = $worksheet->getTableCollection()->count();
|
||||||
|
for ($i = 1; $i <= $tableCount; ++$i) {
|
||||||
|
$this->writeRelationship(
|
||||||
|
$objWriter,
|
||||||
|
'_table_' . $i,
|
||||||
|
'http://schemas.openxmlformats.org/officeDocument/2006/relationships/table',
|
||||||
|
'../tables/table' . $tableRef++ . '.xml'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Write header/footer relationship?
|
// Write header/footer relationship?
|
||||||
$i = 1;
|
$i = 1;
|
||||||
if (count($worksheet->getHeaderFooter()->getImages()) > 0) {
|
if (count($worksheet->getHeaderFooter()->getImages()) > 0) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Table as WorksheetTable;
|
||||||
|
|
||||||
|
class Table extends WriterPart
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Write Table to XML format.
|
||||||
|
*
|
||||||
|
* @param int $tableRef Table ID
|
||||||
|
*
|
||||||
|
* @return string XML Output
|
||||||
|
*/
|
||||||
|
public function writeTable(WorksheetTable $table, $tableRef)
|
||||||
|
{
|
||||||
|
// Create XML writer
|
||||||
|
$objWriter = null;
|
||||||
|
if ($this->getParentWriter()->getUseDiskCaching()) {
|
||||||
|
$objWriter = new XMLWriter(XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory());
|
||||||
|
} else {
|
||||||
|
$objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// XML header
|
||||||
|
$objWriter->startDocument('1.0', 'UTF-8', 'yes');
|
||||||
|
|
||||||
|
// Table
|
||||||
|
$name = 'Table' . $tableRef;
|
||||||
|
$range = $table->getRange();
|
||||||
|
|
||||||
|
$objWriter->startElement('table');
|
||||||
|
$objWriter->writeAttribute('xml:space', 'preserve');
|
||||||
|
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/spreadsheetml/2006/main');
|
||||||
|
$objWriter->writeAttribute('id', (string) $tableRef);
|
||||||
|
$objWriter->writeAttribute('name', $name);
|
||||||
|
$objWriter->writeAttribute('displayName', $table->getName() ?: $name);
|
||||||
|
$objWriter->writeAttribute('ref', $range);
|
||||||
|
$objWriter->writeAttribute('headerRowCount', $table->getShowHeaderRow() ? '1' : '0');
|
||||||
|
$objWriter->writeAttribute('totalsRowCount', $table->getShowTotalsRow() ? '1' : '0');
|
||||||
|
|
||||||
|
// Table Boundaries
|
||||||
|
[$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($table->getRange());
|
||||||
|
|
||||||
|
// Table Auto Filter
|
||||||
|
if ($table->getShowHeaderRow()) {
|
||||||
|
$objWriter->startElement('autoFilter');
|
||||||
|
$objWriter->writeAttribute('ref', $range);
|
||||||
|
foreach (range($rangeStart[0], $rangeEnd[0]) as $offset => $columnIndex) {
|
||||||
|
$column = $table->getColumnByOffset($offset);
|
||||||
|
|
||||||
|
if (!$column->getShowFilterButton()) {
|
||||||
|
$objWriter->startElement('filterColumn');
|
||||||
|
$objWriter->writeAttribute('colId', (string) $offset);
|
||||||
|
$objWriter->writeAttribute('hiddenButton', '1');
|
||||||
|
$objWriter->endElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$objWriter->endElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Table Columns
|
||||||
|
$objWriter->startElement('tableColumns');
|
||||||
|
$objWriter->writeAttribute('count', (string) ($rangeEnd[0] - $rangeStart[0] + 1));
|
||||||
|
foreach (range($rangeStart[0], $rangeEnd[0]) as $offset => $columnIndex) {
|
||||||
|
$worksheet = $table->getWorksheet();
|
||||||
|
if (!$worksheet) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$column = $table->getColumnByOffset($offset);
|
||||||
|
$cell = $worksheet->getCellByColumnAndRow($columnIndex, $rangeStart[1]);
|
||||||
|
|
||||||
|
$objWriter->startElement('tableColumn');
|
||||||
|
$objWriter->writeAttribute('id', (string) ($offset + 1));
|
||||||
|
$objWriter->writeAttribute('name', $table->getShowHeaderRow() ? $cell->getValue() : 'Column' . ($offset + 1));
|
||||||
|
|
||||||
|
if ($table->getShowTotalsRow()) {
|
||||||
|
if ($column->getTotalsRowLabel()) {
|
||||||
|
$objWriter->writeAttribute('totalsRowLabel', $column->getTotalsRowLabel());
|
||||||
|
}
|
||||||
|
if ($column->getTotalsRowFunction()) {
|
||||||
|
$objWriter->writeAttribute('totalsRowFunction', $column->getTotalsRowFunction());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$objWriter->endElement();
|
||||||
|
}
|
||||||
|
$objWriter->endElement();
|
||||||
|
|
||||||
|
// Table Styles
|
||||||
|
$objWriter->startElement('tableStyleInfo');
|
||||||
|
$objWriter->writeAttribute('name', $table->getStyle()->getTheme());
|
||||||
|
$objWriter->writeAttribute('showFirstColumn', $table->getStyle()->getShowFirstColumn() ? '1' : '0');
|
||||||
|
$objWriter->writeAttribute('showLastColumn', $table->getStyle()->getShowLastColumn() ? '1' : '0');
|
||||||
|
$objWriter->writeAttribute('showRowStripes', $table->getStyle()->getShowRowStripes() ? '1' : '0');
|
||||||
|
$objWriter->writeAttribute('showColumnStripes', $table->getStyle()->getShowColumnStripes() ? '1' : '0');
|
||||||
|
$objWriter->endElement();
|
||||||
|
|
||||||
|
$objWriter->endElement();
|
||||||
|
|
||||||
|
// Return
|
||||||
|
return $objWriter->getData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -120,6 +120,9 @@ class Worksheet extends WriterPart
|
||||||
// AlternateContent
|
// AlternateContent
|
||||||
$this->writeAlternateContent($objWriter, $worksheet);
|
$this->writeAlternateContent($objWriter, $worksheet);
|
||||||
|
|
||||||
|
// Table
|
||||||
|
$this->writeTable($objWriter, $worksheet);
|
||||||
|
|
||||||
// ConditionalFormattingRuleExtensionList
|
// ConditionalFormattingRuleExtensionList
|
||||||
// (Must be inserted last. Not insert last, an Excel parse error will occur)
|
// (Must be inserted last. Not insert last, an Excel parse error will occur)
|
||||||
$this->writeExtLst($objWriter, $worksheet);
|
$this->writeExtLst($objWriter, $worksheet);
|
||||||
|
|
@ -993,6 +996,25 @@ class Worksheet extends WriterPart
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write Table.
|
||||||
|
*/
|
||||||
|
private function writeTable(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksheet): void
|
||||||
|
{
|
||||||
|
$tableCount = $worksheet->getTableCollection()->count();
|
||||||
|
|
||||||
|
$objWriter->startElement('tableParts');
|
||||||
|
$objWriter->writeAttribute('count', (string) $tableCount);
|
||||||
|
|
||||||
|
for ($t = 1; $t <= $tableCount; ++$t) {
|
||||||
|
$objWriter->startElement('tablePart');
|
||||||
|
$objWriter->writeAttribute('r:id', 'rId_table_' . $t);
|
||||||
|
$objWriter->endElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
$objWriter->endElement();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write PageSetup.
|
* Write PageSetup.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Worksheet\Table;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Table\Column;
|
||||||
|
|
||||||
|
class ColumnTest extends SetupTeardown
|
||||||
|
{
|
||||||
|
protected function initTable(): Table
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$sheet->getCell('G1')->setValue('Heading');
|
||||||
|
$sheet->getCell('G2')->setValue(2);
|
||||||
|
$sheet->getCell('G3')->setValue(3);
|
||||||
|
$sheet->getCell('G4')->setValue(4);
|
||||||
|
$sheet->getCell('H1')->setValue('Heading2');
|
||||||
|
$sheet->getCell('H2')->setValue(1);
|
||||||
|
$sheet->getCell('H3')->setValue(2);
|
||||||
|
$sheet->getCell('H4')->setValue(3);
|
||||||
|
$this->maxRow = $maxRow = 4;
|
||||||
|
$table = new Table();
|
||||||
|
$table->setRange("G1:H$maxRow");
|
||||||
|
|
||||||
|
return $table;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testVariousGets(): void
|
||||||
|
{
|
||||||
|
$table = $this->initTable();
|
||||||
|
$column = $table->getColumn('H');
|
||||||
|
$result = $column->getColumnIndex();
|
||||||
|
self::assertEquals('H', $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetBadColumnIndex(): void
|
||||||
|
{
|
||||||
|
$this->expectException(PhpSpreadsheetException::class);
|
||||||
|
$this->expectExceptionMessage('Column is outside of current table range.');
|
||||||
|
$table = $this->initTable();
|
||||||
|
$table->getColumn('B');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetColumnIndex(): void
|
||||||
|
{
|
||||||
|
$table = $this->initTable();
|
||||||
|
$column = $table->getColumn('H');
|
||||||
|
$column->setShowFilterButton(false);
|
||||||
|
$expectedResult = 'G';
|
||||||
|
|
||||||
|
$result = $column->setColumnIndex($expectedResult);
|
||||||
|
self::assertInstanceOf(Column::class, $result);
|
||||||
|
|
||||||
|
$result = $result->getColumnIndex();
|
||||||
|
self::assertEquals($expectedResult, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testVariousSets(): void
|
||||||
|
{
|
||||||
|
$table = $this->initTable();
|
||||||
|
$column = $table->getColumn('H');
|
||||||
|
|
||||||
|
$result = $column->setShowFilterButton(false);
|
||||||
|
self::assertInstanceOf(Column::class, $result);
|
||||||
|
self::assertFalse($column->getShowFilterButton());
|
||||||
|
|
||||||
|
$label = 'Total';
|
||||||
|
$result = $column->setTotalsRowLabel($label);
|
||||||
|
self::assertInstanceOf(Column::class, $result);
|
||||||
|
self::assertEquals($label, $column->getTotalsRowLabel());
|
||||||
|
|
||||||
|
$function = 'sum';
|
||||||
|
$result = $column->setTotalsRowFunction($function);
|
||||||
|
self::assertInstanceOf(Column::class, $result);
|
||||||
|
self::assertEquals($function, $column->getTotalsRowFunction());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTable(): void
|
||||||
|
{
|
||||||
|
$table = $this->initTable();
|
||||||
|
$column = new Column('H');
|
||||||
|
$column->setTable($table);
|
||||||
|
self::assertEquals($table, $column->getTable());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Worksheet\Table;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class SetupTeardown extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ?Spreadsheet
|
||||||
|
*/
|
||||||
|
private $spreadsheet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ?Worksheet
|
||||||
|
*/
|
||||||
|
private $sheet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $maxRow = 4;
|
||||||
|
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
$this->sheet = null;
|
||||||
|
if ($this->spreadsheet !== null) {
|
||||||
|
$this->spreadsheet->disconnectWorksheets();
|
||||||
|
$this->spreadsheet = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSpreadsheet(): Spreadsheet
|
||||||
|
{
|
||||||
|
if ($this->spreadsheet !== null) {
|
||||||
|
return $this->spreadsheet;
|
||||||
|
}
|
||||||
|
$this->spreadsheet = new Spreadsheet();
|
||||||
|
|
||||||
|
return $this->spreadsheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSheet(): Worksheet
|
||||||
|
{
|
||||||
|
if ($this->sheet !== null) {
|
||||||
|
return $this->sheet;
|
||||||
|
}
|
||||||
|
$this->sheet = $this->getSpreadsheet()->getActiveSheet();
|
||||||
|
|
||||||
|
return $this->sheet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Worksheet\Table;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Table\TableStyle;
|
||||||
|
|
||||||
|
class TableStyleTest extends SetupTeardown
|
||||||
|
{
|
||||||
|
private const INITIAL_RANGE = 'H2:O256';
|
||||||
|
|
||||||
|
public function testVariousSets(): void
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
$style = $table->getStyle();
|
||||||
|
|
||||||
|
$result = $style->setTheme(TableStyle::TABLE_STYLE_DARK1);
|
||||||
|
self::assertInstanceOf(TableStyle::class, $result);
|
||||||
|
self::assertEquals(TableStyle::TABLE_STYLE_DARK1, $style->getTheme());
|
||||||
|
|
||||||
|
$result = $style->setShowFirstColumn(true);
|
||||||
|
self::assertInstanceOf(TableStyle::class, $result);
|
||||||
|
self::assertTrue($style->getShowFirstColumn());
|
||||||
|
|
||||||
|
$result = $style->setShowLastColumn(true);
|
||||||
|
self::assertInstanceOf(TableStyle::class, $result);
|
||||||
|
self::assertTrue($style->getShowLastColumn());
|
||||||
|
|
||||||
|
$result = $style->setShowRowStripes(true);
|
||||||
|
self::assertInstanceOf(TableStyle::class, $result);
|
||||||
|
self::assertTrue($style->getShowRowStripes());
|
||||||
|
|
||||||
|
$result = $style->setShowColumnStripes(true);
|
||||||
|
self::assertInstanceOf(TableStyle::class, $result);
|
||||||
|
self::assertTrue($style->getShowColumnStripes());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTable(): void
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
$style = new TableStyle();
|
||||||
|
$style->setTable($table);
|
||||||
|
self::assertEquals($table, $style->getTable());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,433 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Worksheet\Table;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Table\Column;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
|
|
||||||
|
class TableTest extends SetupTeardown
|
||||||
|
{
|
||||||
|
private const INITIAL_RANGE = 'H2:O256';
|
||||||
|
|
||||||
|
public function testToString(): void
|
||||||
|
{
|
||||||
|
$expectedResult = self::INITIAL_RANGE;
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
|
||||||
|
// magic __toString should return the active table range
|
||||||
|
$result = (string) $table;
|
||||||
|
self::assertEquals($expectedResult, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testVariousSets(): void
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
|
||||||
|
$result = $table->setName('Table 1');
|
||||||
|
self::assertInstanceOf(Table::class, $result);
|
||||||
|
// Spaces will be converted to underscore
|
||||||
|
self::assertEquals('Table_1', $table->getName());
|
||||||
|
|
||||||
|
$result = $table->setShowHeaderRow(false);
|
||||||
|
self::assertInstanceOf(Table::class, $result);
|
||||||
|
self::assertFalse($table->getShowHeaderRow());
|
||||||
|
|
||||||
|
$result = $table->setShowTotalsRow(true);
|
||||||
|
self::assertInstanceOf(Table::class, $result);
|
||||||
|
self::assertTrue($table->getShowTotalsRow());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetWorksheet(): void
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
$result = $table->getWorksheet();
|
||||||
|
self::assertSame($sheet, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetWorksheet(): void
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
$spreadsheet = $this->getSpreadsheet();
|
||||||
|
$sheet2 = $spreadsheet->createSheet();
|
||||||
|
// Setters return the instance to implement the fluent interface
|
||||||
|
$result = $table->setWorksheet($sheet2);
|
||||||
|
self::assertInstanceOf(Table::class, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetRange(): void
|
||||||
|
{
|
||||||
|
$expectedResult = self::INITIAL_RANGE;
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
|
||||||
|
// Result should be the active table range
|
||||||
|
$result = $table->getRange();
|
||||||
|
self::assertEquals($expectedResult, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetRange(): void
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$title = $sheet->getTitle();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
$ranges = [
|
||||||
|
'G1:J512' => "$title!G1:J512",
|
||||||
|
'K1:N20' => 'K1:N20',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($ranges as $actualRange => $fullRange) {
|
||||||
|
// Setters return the instance to implement the fluent interface
|
||||||
|
$result = $table->setRange($fullRange);
|
||||||
|
self::assertInstanceOf(Table::class, $result);
|
||||||
|
|
||||||
|
// Result should be the new table range
|
||||||
|
$result = $table->getRange();
|
||||||
|
self::assertEquals($actualRange, $result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClearRange(): void
|
||||||
|
{
|
||||||
|
$expectedResult = '';
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
|
||||||
|
// Setters return the instance to implement the fluent interface
|
||||||
|
$result = $table->setRange('');
|
||||||
|
self::assertInstanceOf(Table::class, $result);
|
||||||
|
|
||||||
|
// Result should be a clear range
|
||||||
|
$result = $table->getRange();
|
||||||
|
self::assertEquals($expectedResult, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetRangeInvalidRange(): void
|
||||||
|
{
|
||||||
|
$this->expectException(PhpSpreadsheetException::class);
|
||||||
|
|
||||||
|
$expectedResult = 'A1';
|
||||||
|
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table($expectedResult, $sheet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetColumnsEmpty(): void
|
||||||
|
{
|
||||||
|
// There should be no columns yet defined
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
$result = $table->getColumns();
|
||||||
|
self::assertIsArray($result);
|
||||||
|
self::assertCount(0, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetColumnOffset(): void
|
||||||
|
{
|
||||||
|
$columnIndexes = [
|
||||||
|
'H' => 0,
|
||||||
|
'K' => 3,
|
||||||
|
'M' => 5,
|
||||||
|
];
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
|
||||||
|
// If we request a specific column by its column ID, we should get an
|
||||||
|
// integer returned representing the column offset within the range
|
||||||
|
foreach ($columnIndexes as $columnIndex => $columnOffset) {
|
||||||
|
$result = $table->getColumnOffset($columnIndex);
|
||||||
|
self::assertEquals($columnOffset, $result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetInvalidColumnOffset(): void
|
||||||
|
{
|
||||||
|
$this->expectException(PhpSpreadsheetException::class);
|
||||||
|
|
||||||
|
$invalidColumn = 'G';
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table();
|
||||||
|
$table->setWorksheet($sheet);
|
||||||
|
|
||||||
|
$table->getColumnOffset($invalidColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetColumnWithString(): void
|
||||||
|
{
|
||||||
|
$expectedResult = 'L';
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
|
||||||
|
// Setters return the instance to implement the fluent interface
|
||||||
|
$result = $table->setColumn($expectedResult);
|
||||||
|
self::assertInstanceOf(Table::class, $result);
|
||||||
|
|
||||||
|
$result = $table->getColumns();
|
||||||
|
// Result should be an array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table\Column
|
||||||
|
// objects for each column we set indexed by the column ID
|
||||||
|
self::assertIsArray($result);
|
||||||
|
self::assertCount(1, $result);
|
||||||
|
self::assertArrayHasKey($expectedResult, $result);
|
||||||
|
self::assertInstanceOf(Column::class, $result[$expectedResult]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetInvalidColumnWithString(): void
|
||||||
|
{
|
||||||
|
$this->expectException(PhpSpreadsheetException::class);
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
|
||||||
|
$invalidColumn = 'A';
|
||||||
|
$table->setColumn($invalidColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetColumnWithColumnObject(): void
|
||||||
|
{
|
||||||
|
$expectedResult = 'M';
|
||||||
|
$columnObject = new Column($expectedResult);
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
|
||||||
|
// Setters return the instance to implement the fluent interface
|
||||||
|
$result = $table->setColumn($columnObject);
|
||||||
|
self::assertInstanceOf(Table::class, $result);
|
||||||
|
|
||||||
|
$result = $table->getColumns();
|
||||||
|
// Result should be an array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table\Column
|
||||||
|
// objects for each column we set indexed by the column ID
|
||||||
|
self::assertIsArray($result);
|
||||||
|
self::assertCount(1, $result);
|
||||||
|
self::assertArrayHasKey($expectedResult, $result);
|
||||||
|
self::assertInstanceOf(Column::class, $result[$expectedResult]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetInvalidColumnWithObject(): void
|
||||||
|
{
|
||||||
|
$this->expectException(PhpSpreadsheetException::class);
|
||||||
|
|
||||||
|
$invalidColumn = 'E';
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
$table->setColumn($invalidColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetColumnWithInvalidDataType(): void
|
||||||
|
{
|
||||||
|
$this->expectException(PhpSpreadsheetException::class);
|
||||||
|
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
$invalidColumn = 123.456;
|
||||||
|
// @phpstan-ignore-next-line
|
||||||
|
$table->setColumn($invalidColumn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetColumns(): void
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
|
||||||
|
$columnIndexes = ['L', 'M'];
|
||||||
|
|
||||||
|
foreach ($columnIndexes as $columnIndex) {
|
||||||
|
$table->setColumn($columnIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $table->getColumns();
|
||||||
|
// Result should be an array of \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table\Column
|
||||||
|
// objects for each column we set indexed by the column ID
|
||||||
|
self::assertIsArray($result);
|
||||||
|
self::assertCount(count($columnIndexes), $result);
|
||||||
|
foreach ($columnIndexes as $columnIndex) {
|
||||||
|
self::assertArrayHasKey($columnIndex, $result);
|
||||||
|
self::assertInstanceOf(Column::class, $result[$columnIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$table->setRange('');
|
||||||
|
self::assertCount(0, $table->getColumns());
|
||||||
|
self::assertSame('', $table->getRange());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetColumn(): void
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
|
||||||
|
$columnIndexes = ['L', 'M'];
|
||||||
|
|
||||||
|
foreach ($columnIndexes as $columnIndex) {
|
||||||
|
$table->setColumn($columnIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we request a specific column by its column ID, we should
|
||||||
|
// get a \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table\Column object returned
|
||||||
|
foreach ($columnIndexes as $columnIndex) {
|
||||||
|
$result = $table->getColumn($columnIndex);
|
||||||
|
self::assertInstanceOf(Column::class, $result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetColumnByOffset(): void
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
|
||||||
|
$columnIndexes = [
|
||||||
|
0 => 'H',
|
||||||
|
3 => 'K',
|
||||||
|
5 => 'M',
|
||||||
|
];
|
||||||
|
|
||||||
|
// If we request a specific column by its offset, we should
|
||||||
|
// get a \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table\Column object returned
|
||||||
|
foreach ($columnIndexes as $columnIndex => $columnID) {
|
||||||
|
$result = $table->getColumnByOffset($columnIndex);
|
||||||
|
self::assertInstanceOf(Column::class, $result);
|
||||||
|
self::assertEquals($result->getColumnIndex(), $columnID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetColumnIfNotSet(): void
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
// If we request a specific column by its column ID, we should
|
||||||
|
// get a \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet\Table\Column object returned
|
||||||
|
$result = $table->getColumn('K');
|
||||||
|
self::assertInstanceOf(Column::class, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetColumnWithoutRangeSet(): void
|
||||||
|
{
|
||||||
|
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
|
||||||
|
// Clear the range
|
||||||
|
$table->setRange('');
|
||||||
|
$table->getColumn('A');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClearRangeWithExistingColumns(): void
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
$expectedResult = '';
|
||||||
|
|
||||||
|
$columnIndexes = ['L', 'M', 'N'];
|
||||||
|
foreach ($columnIndexes as $columnIndex) {
|
||||||
|
$table->setColumn($columnIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setters return the instance to implement the fluent interface
|
||||||
|
$result = $table->setRange('');
|
||||||
|
self::assertInstanceOf(Table::class, $result);
|
||||||
|
|
||||||
|
// Range should be cleared
|
||||||
|
$result = $table->getRange();
|
||||||
|
self::assertEquals($expectedResult, $result);
|
||||||
|
|
||||||
|
// Column array should be cleared
|
||||||
|
$result = $table->getColumns();
|
||||||
|
self::assertIsArray($result);
|
||||||
|
self::assertCount(0, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetRangeWithExistingColumns(): void
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
$expectedResult = 'G1:J512';
|
||||||
|
|
||||||
|
// These columns should be retained
|
||||||
|
$columnIndexes1 = ['I', 'J'];
|
||||||
|
foreach ($columnIndexes1 as $columnIndex) {
|
||||||
|
$table->setColumn($columnIndex);
|
||||||
|
}
|
||||||
|
// These columns should be discarded
|
||||||
|
$columnIndexes2 = ['K', 'L', 'M'];
|
||||||
|
foreach ($columnIndexes2 as $columnIndex) {
|
||||||
|
$table->setColumn($columnIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setters return the instance to implement the fluent interface
|
||||||
|
$result = $table->setRange($expectedResult);
|
||||||
|
self::assertInstanceOf(Table::class, $result);
|
||||||
|
|
||||||
|
// Range should be correctly set
|
||||||
|
$result = $table->getRange();
|
||||||
|
self::assertEquals($expectedResult, $result);
|
||||||
|
|
||||||
|
// Only columns that existed in the original range and that
|
||||||
|
// still fall within the new range should be retained
|
||||||
|
$result = $table->getColumns();
|
||||||
|
self::assertIsArray($result);
|
||||||
|
self::assertCount(count($columnIndexes1), $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClone(): void
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
$columnIndexes = ['L', 'M'];
|
||||||
|
|
||||||
|
foreach ($columnIndexes as $columnIndex) {
|
||||||
|
$table->setColumn($columnIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = clone $table;
|
||||||
|
self::assertInstanceOf(Table::class, $result);
|
||||||
|
self::assertSame($table->getRange(), $result->getRange());
|
||||||
|
self::assertNull($result->getWorksheet());
|
||||||
|
self::assertNotNull($table->getWorksheet());
|
||||||
|
self::assertInstanceOf(Worksheet::class, $table->getWorksheet());
|
||||||
|
$tableColumns = $table->getColumns();
|
||||||
|
$resultColumns = $result->getColumns();
|
||||||
|
self::assertIsArray($tableColumns);
|
||||||
|
self::assertIsArray($resultColumns);
|
||||||
|
self::assertCount(2, $tableColumns);
|
||||||
|
self::assertCount(2, $resultColumns);
|
||||||
|
self::assertArrayHasKey('L', $tableColumns);
|
||||||
|
self::assertArrayHasKey('L', $resultColumns);
|
||||||
|
self::assertArrayHasKey('M', $tableColumns);
|
||||||
|
self::assertArrayHasKey('M', $resultColumns);
|
||||||
|
self::assertInstanceOf(Column::class, $tableColumns['L']);
|
||||||
|
self::assertInstanceOf(Column::class, $resultColumns['L']);
|
||||||
|
self::assertInstanceOf(Column::class, $tableColumns['M']);
|
||||||
|
self::assertInstanceOf(Column::class, $resultColumns['M']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoWorksheet(): void
|
||||||
|
{
|
||||||
|
$table = new Table();
|
||||||
|
self::assertNull($table->getWorksheet());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testClearColumn(): void
|
||||||
|
{
|
||||||
|
$sheet = $this->getSheet();
|
||||||
|
$table = new Table(self::INITIAL_RANGE, $sheet);
|
||||||
|
$columnIndexes = ['J', 'K', 'L', 'M'];
|
||||||
|
|
||||||
|
foreach ($columnIndexes as $columnIndex) {
|
||||||
|
$table->setColumn($columnIndex);
|
||||||
|
}
|
||||||
|
$columns = $table->getColumns();
|
||||||
|
self::assertCount(4, $columns);
|
||||||
|
self::assertArrayHasKey('J', $columns);
|
||||||
|
self::assertArrayHasKey('K', $columns);
|
||||||
|
self::assertArrayHasKey('L', $columns);
|
||||||
|
self::assertArrayHasKey('M', $columns);
|
||||||
|
$table->clearColumn('K');
|
||||||
|
$columns = $table->getColumns();
|
||||||
|
self::assertCount(3, $columns);
|
||||||
|
self::assertArrayHasKey('J', $columns);
|
||||||
|
self::assertArrayHasKey('L', $columns);
|
||||||
|
self::assertArrayHasKey('M', $columns);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue