Split Conditional Ranges

Fix #4039. Excel sometimes stores the location for a Conditional as, say, `B1:B10 C1:C10` rather than `B1:C10`. PhpSpreadsheet does not have a problem with this, but internally stores it as separate Conditionals, one for each range. (There may be more than 2.) User would like it stored as a single Conditional. Since the range is used as the index of an array which holds the conditionals, there is no technical reason why this can't be done. And it does seem like being able to change multiple ranges all at once has some advantages, whether you're doing it in PhpSpreadsheet or in Excel itself.

Making such a change in Xlsx Reader showed no problem in Xlsx Reader and Writer tests. A number of problems did show up in CellMatcherTest, and one in WizardFactoryTest. This was good news, because it meant some of our test spreadsheets used the same type of construction as the test spreadsheet supplied with the issue. So, if additional fixes make the test problems go away, I think no new tests are required. And a smattering of source changes did indeed result in a clean test suite once more.

This is a change, but I don't really think it should break anyone. So I don't think it's necessary to add an option  to opt in to the old or new behavior. I could be wrong. I'll leave this ticket open for at least a couple of weeks to see if anyone thinks otherwise.
This commit is contained in:
oleibman
2024-05-23 17:27:37 -07:00
parent 2ed696f0a2
commit 33edddf40d
4 changed files with 44 additions and 9 deletions
@@ -188,10 +188,8 @@ class ConditionalStyles
$conditionalStyles = $this->readStyleRules($cfRules, $xmlExtLst);
// Extract all cell references in $cellRangeReference
$cellBlocks = explode(' ', str_replace('$', '', strtoupper($cellRangeReference)));
foreach ($cellBlocks as $cellBlock) {
$worksheet->getStyle($cellBlock)->setConditionalStyles($conditionalStyles);
}
$cellRangeReference = str_replace('$', '', strtoupper($cellRangeReference));
$worksheet->getStyle($cellRangeReference)->setConditionalStyles($conditionalStyles);
}
}
@@ -60,6 +60,7 @@ class CellMatcher
protected function setReferenceCellForExpressions(string $conditionalRange): void
{
$conditionalRange = str_replace(' ', ',', $conditionalRange);
$conditionalRange = Coordinate::splitRange(str_replace('$', '', strtoupper($conditionalRange)));
[$this->referenceCell] = $conditionalRange[0];
+11 -5
View File
@@ -1422,8 +1422,11 @@ class Worksheet implements IComparable
$cell = $this->getCell($coordinate);
foreach (array_keys($this->conditionalStylesCollection) as $conditionalRange) {
if ($cell->isInRange($conditionalRange)) {
return $this->conditionalStylesCollection[$conditionalRange];
$cellBlocks = explode(' ', $conditionalRange);
foreach ($cellBlocks as $cellBlock) {
if ($cell->isInRange($cellBlock)) {
return $this->conditionalStylesCollection[$conditionalRange];
}
}
}
@@ -1435,8 +1438,11 @@ class Worksheet implements IComparable
$coordinate = strtoupper($coordinate);
$cell = $this->getCell($coordinate);
foreach (array_keys($this->conditionalStylesCollection) as $conditionalRange) {
if ($cell->isInRange($conditionalRange)) {
return $conditionalRange;
$cellBlocks = explode(' ', $conditionalRange);
foreach ($cellBlocks as $cellBlock) {
if ($cell->isInRange($cellBlock)) {
return $conditionalRange;
}
}
}
@@ -1501,7 +1507,7 @@ class Worksheet implements IComparable
*/
public function setConditionalStyles(string $coordinate, array $styles): static
{
$this->conditionalStylesCollection[strtoupper($coordinate)] = $styles;
$this->conditionalStylesCollection[trim(strtoupper($coordinate))] = $styles;
return $this;
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
class Issue4039Test extends \PHPUnit\Framework\TestCase
{
private static string $testbook = 'tests/data/Style/ConditionalFormatting/CellMatcher.xlsx';
public function testSplitRange(): void
{
$reader = new Xlsx();
$spreadsheet = $reader->load(self::$testbook);
$sheet = $spreadsheet->getSheetByNameOrThrow('cellIs Expression');
$expected = [
'A12:D17 A20', // split range
'A22:D27',
'A2:E6',
];
self::assertSame($expected, array_keys($sheet->getConditionalStylesCollection()));
self::assertSame($expected[0], $sheet->getConditionalRange('A20'));
self::assertSame($expected[0], $sheet->getConditionalRange('C15'));
self::assertNull($sheet->getConditionalRange('A19'));
self::assertSame($expected[1], $sheet->getConditionalRange('D25'));
$spreadsheet->disconnectWorksheets();
}
}