Calculate row range for a single-row AutoFilter range

This commit is contained in:
MarkBaker 2022-10-08 13:23:30 +02:00
parent 339a5933c7
commit 720b98a97f
2 changed files with 50 additions and 4 deletions

View File

@ -10,7 +10,6 @@ use PhpOffice\PhpSpreadsheet\Calculation\Internal\WildcardMatch;
use PhpOffice\PhpSpreadsheet\Cell\AddressRange;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Exception;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
@ -176,13 +175,13 @@ class AutoFilter
public function testColumnInRange($column)
{
if (empty($this->range)) {
throw new PhpSpreadsheetException('No autofilter range is defined.');
throw new Exception('No autofilter 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 autofilter range.');
throw new Exception('Column is outside of current autofilter range.');
}
return $columnIndex - $rangeStart[0];
@ -249,7 +248,7 @@ class AutoFilter
} elseif (is_object($columnObjectOrString) && ($columnObjectOrString instanceof AutoFilter\Column)) {
$column = $columnObjectOrString->getColumnIndex();
} else {
throw new PhpSpreadsheetException('Column is not within the autofilter range.');
throw new Exception('Column is not within the autofilter range.');
}
$this->testColumnInRange($column);
@ -1033,6 +1032,8 @@ class AutoFilter
}
}
$rangeEnd[1] = $this->autoExtendRange($rangeStart[1], $rangeEnd[1]);
// Execute the column tests for each row in the autoFilter range to determine show/hide,
for ($row = $rangeStart[1] + 1; $row <= $rangeEnd[1]; ++$row) {
$result = true;
@ -1055,6 +1056,29 @@ class AutoFilter
return $this;
}
/**
* Magic Range Auto-sizing.
* For a single row rangeSet, we follow MS Excel rules, and search for the first empty row to determine our range.
*/
public function autoExtendRange(int $startRow, int $endRow): int
{
if ($startRow === $endRow && $this->workSheet !== null) {
try {
$rowIterator = $this->workSheet->getRowIterator($startRow + 1);
} catch (Exception $e) {
// If there are no rows below $startRow
return $startRow;
}
foreach ($rowIterator as $row) {
if ($row->isEmpty(CellIterator::TREAT_NULL_VALUE_AS_EMPTY_CELL | CellIterator::TREAT_EMPTY_STRING_AS_EMPTY_CELL) === true) {
return $row->getRowIndex() - 1;
}
}
}
return $endRow;
}
/**
* Implement PHP __clone to create a deep clone, not just a shallow copy.
*/

View File

@ -510,4 +510,26 @@ class AutoFilterTest extends SetupTeardown
self::assertArrayHasKey('K', $columns);
self::assertArrayHasKey('M', $columns);
}
public function testAutoExtendRange(): void
{
$spreadsheet = $this->getSpreadsheet();
$worksheet = $spreadsheet->addSheet(new Worksheet($spreadsheet, 'Autosized AutoFilter'));
$worksheet->getCell('A1')->setValue('Col 1');
$worksheet->getCell('B1')->setValue('Col 2');
$worksheet->setAutoFilter('A1:B1');
$lastRow = $worksheet->getAutoFilter()->autoExtendRange(1, 1);
self::assertSame(1, $lastRow, 'No data below AutoFilter, so there should ne no resize');
$lastRow = $worksheet->getAutoFilter()->autoExtendRange(1, 999);
self::assertSame(999, $lastRow, 'Filter range is already correctly sized');
$data = [['A', 'A'], ['B', 'A'], ['A', 'B'], ['C', 'B'], ['B', null], [null, null], ['D', 'D'], ['E', 'E']];
$worksheet->fromArray($data, null, 'A2', true);
$lastRow = $worksheet->getAutoFilter()->autoExtendRange(1, 1);
self::assertSame(6, $lastRow, 'Filter range has been re-sized incorrectly');
}
}