First steps in the implementation of AutoFilters for ODS Reader and Writer (#2053)
* First steps in the implementation of AutoFilters for ODS Reader and Writer, starting with reading a basic AutoFilter range (ignoring row visibility, filter types and active filters for the moment). And also some additional refactoring to extract the DefinedNames Reader into its own dedicated class as a part of overall code improvement... on the principle of "when working on a class, always try to leave the library codebase in a better state than you found it" * Provide a basic Ods Writer implementation for AutoFilters * AutoFilter Reader Test * AutoFilter Writer Test * Update Change Log
This commit is contained in:
parent
defef9a4ff
commit
83e55cffcc
|
|
@ -9,7 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org).
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Implemented basic AutoFiltering for Ods Reader and Writer [PR #2053](https://github.com/PHPOffice/PhpSpreadsheet/pull/2053)
|
||||||
- Improved support for Row and Column ranges in formulae [Issue #1755](https://github.com/PHPOffice/PhpSpreadsheet/issues/1755) [PR #2028](https://github.com/PHPOffice/PhpSpreadsheet/pull/2028)
|
- Improved support for Row and Column ranges in formulae [Issue #1755](https://github.com/PHPOffice/PhpSpreadsheet/issues/1755) [PR #2028](https://github.com/PHPOffice/PhpSpreadsheet/pull/2028)
|
||||||
|
- Implemented URLENCODE() Web Function
|
||||||
- Implemented the CHITEST(), CHISQ.DIST() and CHISQ.INV() and equivalent Statistical functions, for both left- and right-tailed distributions.
|
- Implemented the CHITEST(), CHISQ.DIST() and CHISQ.INV() and equivalent Statistical functions, for both left- and right-tailed distributions.
|
||||||
- Support for ActiveSheet and SelectedCells in the ODS Reader and Writer. [PR #1908](https://github.com/PHPOffice/PhpSpreadsheet/pull/1908)
|
- Support for ActiveSheet and SelectedCells in the ODS Reader and Writer. [PR #1908](https://github.com/PHPOffice/PhpSpreadsheet/pull/1908)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1313,13 +1313,13 @@
|
||||||
<td style="text-align: center; color: orange;">●</td>
|
<td style="text-align: center; color: orange;">●</td>
|
||||||
<td style="text-align: center; color: orange;">●</td>
|
<td style="text-align: center; color: orange;">●</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td style="text-align: center; color: orange;">●</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td style="text-align: center; color: orange;">●</td>
|
<td style="text-align: center; color: orange;">●</td>
|
||||||
<td style="text-align: center; color: orange;">●</td>
|
<td style="text-align: center; color: orange;">●</td>
|
||||||
<td></td>
|
<td style="text-align: center; color: orange;">●</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td></td>
|
<td></td>
|
||||||
|
|
|
||||||
|
|
@ -2812,12 +2812,7 @@ parameters:
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#"
|
message: "#^Parameter \\#3 \\$subject of function preg_replace expects array\\|string, string\\|null given\\.$#"
|
||||||
count: 7
|
count: 3
|
||||||
path: src/PhpSpreadsheet/Reader/Ods.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\:\\:convertToExcelAddressValue\\(\\) should return string but returns string\\|null\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Reader/Ods.php
|
path: src/PhpSpreadsheet/Reader/Ods.php
|
||||||
|
|
||||||
-
|
-
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,9 @@ use DOMNode;
|
||||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||||
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||||||
use PhpOffice\PhpSpreadsheet\DefinedName;
|
|
||||||
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
|
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Reader\Ods\AutoFilter;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Reader\Ods\DefinedNames;
|
||||||
use PhpOffice\PhpSpreadsheet\Reader\Ods\PageSettings;
|
use PhpOffice\PhpSpreadsheet\Reader\Ods\PageSettings;
|
||||||
use PhpOffice\PhpSpreadsheet\Reader\Ods\Properties as DocumentProperties;
|
use PhpOffice\PhpSpreadsheet\Reader\Ods\Properties as DocumentProperties;
|
||||||
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
||||||
|
|
@ -22,7 +23,6 @@ use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||||
use PhpOffice\PhpSpreadsheet\Shared\File;
|
use PhpOffice\PhpSpreadsheet\Shared\File;
|
||||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
|
||||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use XMLReader;
|
use XMLReader;
|
||||||
use ZipArchive;
|
use ZipArchive;
|
||||||
|
|
@ -304,8 +304,10 @@ class Ods extends BaseReader
|
||||||
|
|
||||||
$pageSettings->readStyleCrossReferences($dom);
|
$pageSettings->readStyleCrossReferences($dom);
|
||||||
|
|
||||||
// Content
|
$autoFilterReader = new AutoFilter($spreadsheet, $tableNs);
|
||||||
|
$definedNameReader = new DefinedNames($spreadsheet, $tableNs);
|
||||||
|
|
||||||
|
// Content
|
||||||
$spreadsheets = $dom->getElementsByTagNameNS($officeNs, 'body')
|
$spreadsheets = $dom->getElementsByTagNameNS($officeNs, 'body')
|
||||||
->item(0)
|
->item(0)
|
||||||
->getElementsByTagNameNS($officeNs, 'spreadsheet');
|
->getElementsByTagNameNS($officeNs, 'spreadsheet');
|
||||||
|
|
@ -642,8 +644,8 @@ class Ods extends BaseReader
|
||||||
++$worksheetID;
|
++$worksheetID;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->readDefinedRanges($spreadsheet, $workbookData, $tableNs);
|
$autoFilterReader->read($workbookData);
|
||||||
$this->readDefinedExpressions($spreadsheet, $workbookData, $tableNs);
|
$definedNameReader->read($workbookData);
|
||||||
}
|
}
|
||||||
$spreadsheet->setActiveSheetIndex(0);
|
$spreadsheet->setActiveSheetIndex(0);
|
||||||
|
|
||||||
|
|
@ -771,26 +773,6 @@ class Ods extends BaseReader
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function convertToExcelAddressValue(string $openOfficeAddress): string
|
|
||||||
{
|
|
||||||
$excelAddress = $openOfficeAddress;
|
|
||||||
|
|
||||||
// Cell range 3-d reference
|
|
||||||
// As we don't support 3-d ranges, we're just going to take a quick and dirty approach
|
|
||||||
// and assume that the second worksheet reference is the same as the first
|
|
||||||
$excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+):\$?([^\.]+)\.([^\.]+)/miu', '$1!$2:$4', $excelAddress);
|
|
||||||
// Cell range reference in another sheet
|
|
||||||
$excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+):\.([^\.]+)/miu', '$1!$2:$3', $excelAddress);
|
|
||||||
// Cell reference in another sheet
|
|
||||||
$excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+)/miu', '$1!$2', $excelAddress);
|
|
||||||
// Cell range reference
|
|
||||||
$excelAddress = preg_replace('/\.([^\.]+):\.([^\.]+)/miu', '$1:$2', $excelAddress);
|
|
||||||
// Simple cell reference
|
|
||||||
$excelAddress = preg_replace('/\.([^\.]+)/miu', '$1', $excelAddress);
|
|
||||||
|
|
||||||
return $excelAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function convertToExcelFormulaValue(string $openOfficeFormula): string
|
private function convertToExcelFormulaValue(string $openOfficeFormula): string
|
||||||
{
|
{
|
||||||
$temp = explode('"', $openOfficeFormula);
|
$temp = explode('"', $openOfficeFormula);
|
||||||
|
|
@ -816,53 +798,4 @@ class Ods extends BaseReader
|
||||||
|
|
||||||
return $excelFormula;
|
return $excelFormula;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Read any Named Ranges that are defined in this spreadsheet.
|
|
||||||
*/
|
|
||||||
private function readDefinedRanges(Spreadsheet $spreadsheet, DOMElement $workbookData, string $tableNs): void
|
|
||||||
{
|
|
||||||
$namedRanges = $workbookData->getElementsByTagNameNS($tableNs, 'named-range');
|
|
||||||
foreach ($namedRanges as $definedNameElement) {
|
|
||||||
$definedName = $definedNameElement->getAttributeNS($tableNs, 'name');
|
|
||||||
$baseAddress = $definedNameElement->getAttributeNS($tableNs, 'base-cell-address');
|
|
||||||
$range = $definedNameElement->getAttributeNS($tableNs, 'cell-range-address');
|
|
||||||
|
|
||||||
$baseAddress = $this->convertToExcelAddressValue($baseAddress);
|
|
||||||
$range = $this->convertToExcelAddressValue($range);
|
|
||||||
|
|
||||||
$this->addDefinedName($spreadsheet, $baseAddress, $definedName, $range);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read any Named Formulae that are defined in this spreadsheet.
|
|
||||||
*/
|
|
||||||
private function readDefinedExpressions(Spreadsheet $spreadsheet, DOMElement $workbookData, string $tableNs): void
|
|
||||||
{
|
|
||||||
$namedExpressions = $workbookData->getElementsByTagNameNS($tableNs, 'named-expression');
|
|
||||||
foreach ($namedExpressions as $definedNameElement) {
|
|
||||||
$definedName = $definedNameElement->getAttributeNS($tableNs, 'name');
|
|
||||||
$baseAddress = $definedNameElement->getAttributeNS($tableNs, 'base-cell-address');
|
|
||||||
$expression = $definedNameElement->getAttributeNS($tableNs, 'expression');
|
|
||||||
|
|
||||||
$baseAddress = $this->convertToExcelAddressValue($baseAddress);
|
|
||||||
$expression = $this->convertToExcelFormulaValue($expression);
|
|
||||||
|
|
||||||
$this->addDefinedName($spreadsheet, $baseAddress, $definedName, $expression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assess scope and store the Defined Name.
|
|
||||||
*/
|
|
||||||
private function addDefinedName(Spreadsheet $spreadsheet, string $baseAddress, string $definedName, string $value): void
|
|
||||||
{
|
|
||||||
[$sheetReference] = Worksheet::extractSheetTitle($baseAddress, true);
|
|
||||||
$worksheet = $spreadsheet->getSheetByName($sheetReference);
|
|
||||||
// Worksheet might still be null if we're only loading selected sheets rather than the full spreadsheet
|
|
||||||
if ($worksheet !== null) {
|
|
||||||
$spreadsheet->addDefinedName(DefinedName::createInstance((string) $definedName, $worksheet, $value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheet\Reader\Ods;
|
||||||
|
|
||||||
|
use DOMElement;
|
||||||
|
use DOMNode;
|
||||||
|
|
||||||
|
class AutoFilter extends BaseReader
|
||||||
|
{
|
||||||
|
public function read(DOMElement $workbookData): void
|
||||||
|
{
|
||||||
|
$this->readAutoFilters($workbookData);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function readAutoFilters(DOMElement $workbookData): void
|
||||||
|
{
|
||||||
|
$databases = $workbookData->getElementsByTagNameNS($this->tableNs, 'database-ranges');
|
||||||
|
|
||||||
|
foreach ($databases as $autofilters) {
|
||||||
|
foreach ($autofilters->childNodes as $autofilter) {
|
||||||
|
$autofilterRange = $this->getAttributeValue($autofilter, 'target-range-address');
|
||||||
|
if ($autofilterRange !== null) {
|
||||||
|
$baseAddress = $this->convertToExcelAddressValue($autofilterRange);
|
||||||
|
$this->spreadsheet->getActiveSheet()->setAutoFilter($baseAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getAttributeValue(?DOMNode $node, string $attributeName): ?string
|
||||||
|
{
|
||||||
|
if ($node !== null && $node->attributes !== null) {
|
||||||
|
$attribute = $node->attributes->getNamedItemNS(
|
||||||
|
$this->tableNs,
|
||||||
|
$attributeName
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($attribute !== null) {
|
||||||
|
return $attribute->nodeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheet\Reader\Ods;
|
||||||
|
|
||||||
|
use DOMElement;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
|
||||||
|
abstract class BaseReader
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Spreadsheet
|
||||||
|
*/
|
||||||
|
protected $spreadsheet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $tableNs;
|
||||||
|
|
||||||
|
public function __construct(Spreadsheet $spreadsheet, string $tableNs)
|
||||||
|
{
|
||||||
|
$this->spreadsheet = $spreadsheet;
|
||||||
|
$this->tableNs = $tableNs;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function read(DOMElement $workbookData): void;
|
||||||
|
|
||||||
|
protected function convertToExcelAddressValue(string $openOfficeAddress): string
|
||||||
|
{
|
||||||
|
$excelAddress = $openOfficeAddress;
|
||||||
|
|
||||||
|
// Cell range 3-d reference
|
||||||
|
// As we don't support 3-d ranges, we're just going to take a quick and dirty approach
|
||||||
|
// and assume that the second worksheet reference is the same as the first
|
||||||
|
$excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+):\$?([^\.]+)\.([^\.]+)/miu', '$1!$2:$4', $excelAddress);
|
||||||
|
// Cell range reference in another sheet
|
||||||
|
$excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+):\.([^\.]+)/miu', '$1!$2:$3', $excelAddress ?? '');
|
||||||
|
// Cell reference in another sheet
|
||||||
|
$excelAddress = preg_replace('/\$?([^\.]+)\.([^\.]+)/miu', '$1!$2', $excelAddress ?? '');
|
||||||
|
// Cell range reference
|
||||||
|
$excelAddress = preg_replace('/\.([^\.]+):\.([^\.]+)/miu', '$1:$2', $excelAddress ?? '');
|
||||||
|
// Simple cell reference
|
||||||
|
$excelAddress = preg_replace('/\.([^\.]+)/miu', '$1', $excelAddress ?? '');
|
||||||
|
|
||||||
|
return $excelAddress ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function convertToExcelFormulaValue(string $openOfficeFormula): string
|
||||||
|
{
|
||||||
|
$temp = explode('"', $openOfficeFormula);
|
||||||
|
$tKey = false;
|
||||||
|
foreach ($temp as &$value) {
|
||||||
|
// @var string $value
|
||||||
|
// Only replace in alternate array entries (i.e. non-quoted blocks)
|
||||||
|
if ($tKey = !$tKey) {
|
||||||
|
// Cell range reference in another sheet
|
||||||
|
$value = preg_replace('/\[\$?([^\.]+)\.([^\.]+):\.([^\.]+)\]/miu', '$1!$2:$3', $value);
|
||||||
|
// Cell reference in another sheet
|
||||||
|
$value = preg_replace('/\[\$?([^\.]+)\.([^\.]+)\]/miu', '$1!$2', $value ?? '');
|
||||||
|
// Cell range reference
|
||||||
|
$value = preg_replace('/\[\.([^\.]+):\.([^\.]+)\]/miu', '$1:$2', $value ?? '');
|
||||||
|
// Simple cell reference
|
||||||
|
$value = preg_replace('/\[\.([^\.]+)\]/miu', '$1', $value ?? '');
|
||||||
|
|
||||||
|
$value = Calculation::translateSeparator(';', ',', $value ?? '', $inBraces);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then rebuild the formula string
|
||||||
|
$excelFormula = implode('"', $temp);
|
||||||
|
|
||||||
|
return $excelFormula;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheet\Reader\Ods;
|
||||||
|
|
||||||
|
use DOMElement;
|
||||||
|
use PhpOffice\PhpSpreadsheet\DefinedName;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
|
|
||||||
|
class DefinedNames extends BaseReader
|
||||||
|
{
|
||||||
|
public function read(DOMElement $workbookData): void
|
||||||
|
{
|
||||||
|
$this->readDefinedRanges($workbookData);
|
||||||
|
$this->readDefinedExpressions($workbookData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read any Named Ranges that are defined in this spreadsheet.
|
||||||
|
*/
|
||||||
|
protected function readDefinedRanges(DOMElement $workbookData): void
|
||||||
|
{
|
||||||
|
$namedRanges = $workbookData->getElementsByTagNameNS($this->tableNs, 'named-range');
|
||||||
|
foreach ($namedRanges as $definedNameElement) {
|
||||||
|
$definedName = $definedNameElement->getAttributeNS($this->tableNs, 'name');
|
||||||
|
$baseAddress = $definedNameElement->getAttributeNS($this->tableNs, 'base-cell-address');
|
||||||
|
$range = $definedNameElement->getAttributeNS($this->tableNs, 'cell-range-address');
|
||||||
|
|
||||||
|
$baseAddress = $this->convertToExcelAddressValue($baseAddress);
|
||||||
|
$range = $this->convertToExcelAddressValue($range);
|
||||||
|
|
||||||
|
$this->addDefinedName($baseAddress, $definedName, $range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read any Named Formulae that are defined in this spreadsheet.
|
||||||
|
*/
|
||||||
|
protected function readDefinedExpressions(DOMElement $workbookData): void
|
||||||
|
{
|
||||||
|
$namedExpressions = $workbookData->getElementsByTagNameNS($this->tableNs, 'named-expression');
|
||||||
|
foreach ($namedExpressions as $definedNameElement) {
|
||||||
|
$definedName = $definedNameElement->getAttributeNS($this->tableNs, 'name');
|
||||||
|
$baseAddress = $definedNameElement->getAttributeNS($this->tableNs, 'base-cell-address');
|
||||||
|
$expression = $definedNameElement->getAttributeNS($this->tableNs, 'expression');
|
||||||
|
|
||||||
|
$baseAddress = $this->convertToExcelAddressValue($baseAddress);
|
||||||
|
$expression = $this->convertToExcelFormulaValue($expression);
|
||||||
|
|
||||||
|
$this->addDefinedName($baseAddress, $definedName, $expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assess scope and store the Defined Name.
|
||||||
|
*/
|
||||||
|
private function addDefinedName(string $baseAddress, string $definedName, string $value): void
|
||||||
|
{
|
||||||
|
[$sheetReference] = Worksheet::extractSheetTitle($baseAddress, true);
|
||||||
|
$worksheet = $this->spreadsheet->getSheetByName($sheetReference);
|
||||||
|
// Worksheet might still be null if we're only loading selected sheets rather than the full spreadsheet
|
||||||
|
if ($worksheet !== null) {
|
||||||
|
$this->spreadsheet->addDefinedName(DefinedName::createInstance((string) $definedName, $worksheet, $value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheet\Writer\Ods;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
|
|
||||||
|
class AutoFilters
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var XMLWriter
|
||||||
|
*/
|
||||||
|
private $objWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Spreadsheet
|
||||||
|
*/
|
||||||
|
private $spreadsheet;
|
||||||
|
|
||||||
|
public function __construct(XMLWriter $objWriter, Spreadsheet $spreadsheet)
|
||||||
|
{
|
||||||
|
$this->objWriter = $objWriter;
|
||||||
|
$this->spreadsheet = $spreadsheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write(): void
|
||||||
|
{
|
||||||
|
$wrapperWritten = false;
|
||||||
|
$sheetCount = $this->spreadsheet->getSheetCount();
|
||||||
|
for ($i = 0; $i < $sheetCount; ++$i) {
|
||||||
|
$worksheet = $this->spreadsheet->getSheet($i);
|
||||||
|
$autofilter = $worksheet->getAutoFilter();
|
||||||
|
if ($autofilter !== null && !empty($autofilter->getRange())) {
|
||||||
|
if ($wrapperWritten === false) {
|
||||||
|
$this->objWriter->startElement('table:database-ranges');
|
||||||
|
$wrapperWritten = true;
|
||||||
|
}
|
||||||
|
$this->objWriter->startElement('table:database-range');
|
||||||
|
$this->objWriter->writeAttribute('table:orientation', 'column');
|
||||||
|
$this->objWriter->writeAttribute('table:display-filter-buttons', 'true');
|
||||||
|
$this->objWriter->writeAttribute(
|
||||||
|
'table:target-range-address',
|
||||||
|
$this->formatRange($worksheet, $autofilter)
|
||||||
|
);
|
||||||
|
$this->objWriter->endElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($wrapperWritten === true) {
|
||||||
|
$this->objWriter->endElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function formatRange(Worksheet $worksheet, Autofilter $autofilter): string
|
||||||
|
{
|
||||||
|
$title = $worksheet->getTitle();
|
||||||
|
$range = $autofilter->getRange();
|
||||||
|
|
||||||
|
return "'{$title}'.{$range}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -101,6 +101,7 @@ class Content extends WriterPart
|
||||||
|
|
||||||
$this->writeSheets($objWriter);
|
$this->writeSheets($objWriter);
|
||||||
|
|
||||||
|
(new AutoFilters($objWriter, $this->getParentWriter()->getSpreadsheet()))->write();
|
||||||
// Defined names (ranges and formulae)
|
// Defined names (ranges and formulae)
|
||||||
(new NamedExpressions($objWriter, $this->getParentWriter()->getSpreadsheet(), $this->formulaConvertor))->write();
|
(new NamedExpressions($objWriter, $this->getParentWriter()->getSpreadsheet(), $this->formulaConvertor))->write();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Reader\Ods;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Reader\Ods;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class AutoFilterTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Spreadsheet
|
||||||
|
*/
|
||||||
|
private $spreadsheet;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$filename = 'tests/data/Reader/Ods/AutoFilter.ods';
|
||||||
|
$reader = new Ods();
|
||||||
|
$this->spreadsheet = $reader->load($filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAutoFilterRange(): void
|
||||||
|
{
|
||||||
|
$worksheet = $this->spreadsheet->getActiveSheet();
|
||||||
|
|
||||||
|
$autoFilterRange = $worksheet->getAutoFilter()->getRange();
|
||||||
|
|
||||||
|
self::assertSame('A1:C9', $autoFilterRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Writer\Ods;
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
|
||||||
|
|
||||||
|
class AutoFilterTest extends AbstractFunctional
|
||||||
|
{
|
||||||
|
public function testAutoFilterWriter(): void
|
||||||
|
{
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$worksheet = $spreadsheet->getActiveSheet();
|
||||||
|
|
||||||
|
$dataSet = [
|
||||||
|
['Year', 'Quarter', 'Sales'],
|
||||||
|
[2020, 'Q1', 100],
|
||||||
|
[2020, 'Q2', 120],
|
||||||
|
[2020, 'Q3', 140],
|
||||||
|
[2020, 'Q4', 160],
|
||||||
|
[2021, 'Q1', 180],
|
||||||
|
[2021, 'Q2', 75],
|
||||||
|
[2021, 'Q3', 0],
|
||||||
|
[2021, 'Q4', 0],
|
||||||
|
];
|
||||||
|
$worksheet->fromArray($dataSet, null, 'A1');
|
||||||
|
$worksheet->getAutoFilter()->setRange('A1:C9');
|
||||||
|
|
||||||
|
$reloaded = $this->writeAndReload($spreadsheet, 'Ods');
|
||||||
|
|
||||||
|
self::assertSame('A1:C9', $reloaded->getActiveSheet()->getAutoFilter()->getRange());
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Loading…
Reference in New Issue