Skip to content

Commit f943e2d

Browse files
committed
Allow Multiple Ranges
1 parent 222bf51 commit f943e2d

File tree

7 files changed

+103
-51
lines changed

7 files changed

+103
-51
lines changed

src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,26 +37,23 @@ public function load(): void
3737
foreach ($this->worksheetXml->dataValidations->dataValidation as $dataValidation) {
3838
// Uppercase coordinate
3939
$range = strtoupper((string) $dataValidation['sqref']);
40-
$rangeSet = explode(' ', $range);
41-
foreach ($rangeSet as $range) {
42-
$docValidation = new DataValidation();
43-
$docValidation->setType((string) $dataValidation['type']);
44-
$docValidation->setErrorStyle((string) $dataValidation['errorStyle']);
45-
$docValidation->setOperator((string) $dataValidation['operator']);
46-
$docValidation->setAllowBlank(filter_var($dataValidation['allowBlank'], FILTER_VALIDATE_BOOLEAN));
47-
// showDropDown is inverted (works as hideDropDown if true)
48-
$docValidation->setShowDropDown(!filter_var($dataValidation['showDropDown'], FILTER_VALIDATE_BOOLEAN));
49-
$docValidation->setShowInputMessage(filter_var($dataValidation['showInputMessage'], FILTER_VALIDATE_BOOLEAN));
50-
$docValidation->setShowErrorMessage(filter_var($dataValidation['showErrorMessage'], FILTER_VALIDATE_BOOLEAN));
51-
$docValidation->setErrorTitle((string) $dataValidation['errorTitle']);
52-
$docValidation->setError((string) $dataValidation['error']);
53-
$docValidation->setPromptTitle((string) $dataValidation['promptTitle']);
54-
$docValidation->setPrompt((string) $dataValidation['prompt']);
55-
$docValidation->setFormula1(Xlsx::replacePrefixes((string) $dataValidation->formula1));
56-
$docValidation->setFormula2(Xlsx::replacePrefixes((string) $dataValidation->formula2));
57-
$docValidation->setSqref($range);
58-
$this->worksheet->setDataValidation($range, $docValidation);
59-
}
40+
$docValidation = new DataValidation();
41+
$docValidation->setType((string) $dataValidation['type']);
42+
$docValidation->setErrorStyle((string) $dataValidation['errorStyle']);
43+
$docValidation->setOperator((string) $dataValidation['operator']);
44+
$docValidation->setAllowBlank(filter_var($dataValidation['allowBlank'], FILTER_VALIDATE_BOOLEAN));
45+
// showDropDown is inverted (works as hideDropDown if true)
46+
$docValidation->setShowDropDown(!filter_var($dataValidation['showDropDown'], FILTER_VALIDATE_BOOLEAN));
47+
$docValidation->setShowInputMessage(filter_var($dataValidation['showInputMessage'], FILTER_VALIDATE_BOOLEAN));
48+
$docValidation->setShowErrorMessage(filter_var($dataValidation['showErrorMessage'], FILTER_VALIDATE_BOOLEAN));
49+
$docValidation->setErrorTitle((string) $dataValidation['errorTitle']);
50+
$docValidation->setError((string) $dataValidation['error']);
51+
$docValidation->setPromptTitle((string) $dataValidation['promptTitle']);
52+
$docValidation->setPrompt((string) $dataValidation['prompt']);
53+
$docValidation->setFormula1(Xlsx::replacePrefixes((string) $dataValidation->formula1));
54+
$docValidation->setFormula2(Xlsx::replacePrefixes((string) $dataValidation->formula2));
55+
$docValidation->setSqref($range);
56+
$this->worksheet->setDataValidation($range, $docValidation);
6057
}
6158
}
6259
}

src/PhpSpreadsheet/ReferenceHelper.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,13 @@ protected function adjustDataValidations(Worksheet $worksheet, int $numberOfColu
282282
)
283283
);
284284
}
285-
$newReference = $this->updateCellReference($cellAddress);
285+
$addressParts = explode(' ', $cellAddress);
286+
$newReference = '';
287+
$separator = '';
288+
foreach ($addressParts as $addressPart) {
289+
$newReference .= $separator . $this->updateCellReference($addressPart);
290+
$separator = ' ';
291+
}
286292
if ($cellAddress !== $newReference) {
287293
$dataValidation->setSqref($newReference);
288294
$worksheet->setDataValidation($newReference, $dataValidation);

src/PhpSpreadsheet/Worksheet/Worksheet.php

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ class Worksheet
296296

297297
/**
298298
* Data validation objects. Indexed by cell coordinate, e.g. 'A1'.
299+
* Index can include ranges, and multiple cells/ranges.
299300
*/
300301
private array $dataValidationCollection = [];
301302

@@ -3261,23 +3262,30 @@ public function getHyperlinkCollection(): array
32613262
*/
32623263
public function getDataValidation(string $cellCoordinate): DataValidation
32633264
{
3264-
// return data validation if we already have one
32653265
if (isset($this->dataValidationCollection[$cellCoordinate])) {
32663266
return $this->dataValidationCollection[$cellCoordinate];
32673267
}
3268-
foreach ($this->dataValidationCollection as $dataValidation) {
3269-
$sqref = $dataValidation->getSqref() ?? '';
3270-
if (str_contains($sqref, ':')) {
3271-
if (Coordinate::coordinateIsInsideRange($sqref, $cellCoordinate)) {
3268+
3269+
foreach ($this->dataValidationCollection as $key => $dataValidation) {
3270+
$keyParts = explode(' ', $key);
3271+
foreach ($keyParts as $keyPart) {
3272+
if ($keyPart === $cellCoordinate) {
32723273
return $dataValidation;
32733274
}
3275+
if (str_contains($keyPart, ':')) {
3276+
if (Coordinate::coordinateIsInsideRange($keyPart, $cellCoordinate)) {
3277+
return $dataValidation;
3278+
}
3279+
}
32743280
}
32753281
}
32763282

32773283
// else create data validation
3278-
$this->dataValidationCollection[$cellCoordinate] = new DataValidation();
3284+
$dataValidation = new DataValidation();
3285+
$dataValidation->setSqref($cellCoordinate);
3286+
$this->dataValidationCollection[$cellCoordinate] = $dataValidation;
32793287

3280-
return $this->dataValidationCollection[$cellCoordinate];
3288+
return $dataValidation;
32813289
}
32823290

32833291
/**
@@ -3308,12 +3316,17 @@ public function dataValidationExists(string $coordinate): bool
33083316
if (isset($this->dataValidationCollection[$coordinate])) {
33093317
return true;
33103318
}
3311-
foreach ($this->dataValidationCollection as $dataValidation) {
3312-
$sqref = $dataValidation->getSqref() ?? '';
3313-
if (str_contains($sqref, ':')) {
3314-
if (Coordinate::coordinateIsInsideRange($sqref, $coordinate)) {
3319+
foreach ($this->dataValidationCollection as $key => $dataValidation) {
3320+
$keyParts = explode(' ', $key);
3321+
foreach ($keyParts as $keyPart) {
3322+
if ($keyPart === $coordinate) {
33153323
return true;
33163324
}
3325+
if (str_contains($keyPart, ':')) {
3326+
if (Coordinate::coordinateIsInsideRange($keyPart, $coordinate)) {
3327+
return true;
3328+
}
3329+
}
33173330
}
33183331
}
33193332

@@ -3327,7 +3340,17 @@ public function dataValidationExists(string $coordinate): bool
33273340
*/
33283341
public function getDataValidationCollection(): array
33293342
{
3330-
return $this->dataValidationCollection;
3343+
$collectionCells = [];
3344+
$collectionRanges = [];
3345+
foreach ($this->dataValidationCollection as $key => $dataValidation) {
3346+
if (preg_match('/[: ]/', $key) === 1) {
3347+
$collectionRanges[$key] = $dataValidation;
3348+
} else {
3349+
$collectionCells[$key] = $dataValidation;
3350+
}
3351+
}
3352+
3353+
return array_merge($collectionCells, $collectionRanges);
33313354
}
33323355

33333356
/**

src/PhpSpreadsheet/Writer/Xls/Worksheet.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2605,7 +2605,14 @@ private function writeMsoDrawing(): void
26052605
private function writeDataValidity(): void
26062606
{
26072607
// Datavalidation collection
2608-
$dataValidationCollection = $this->phpSheet->getDataValidationCollection();
2608+
$dataValidationCollection1 = $this->phpSheet->getDataValidationCollection();
2609+
$dataValidationCollection = [];
2610+
foreach ($dataValidationCollection1 as $key => $dataValidation) {
2611+
$keyParts = explode(' ', $key);
2612+
foreach ($keyParts as $keyPart) {
2613+
$dataValidationCollection[$keyPart] = $dataValidation;
2614+
}
2615+
}
26092616

26102617
// Write data validations?
26112618
if (!empty($dataValidationCollection)) {

src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -943,7 +943,6 @@ private function writeDataValidations(XMLWriter $objWriter, PhpspreadsheetWorksh
943943

944944
// Write data validations?
945945
if (!empty($dataValidationCollection)) {
946-
$dataValidationCollection = Coordinate::mergeRangesInCollection($dataValidationCollection);
947946
$objWriter->startElement('dataValidations');
948947
$objWriter->writeAttribute('count', (string) count($dataValidationCollection));
949948

tests/PhpSpreadsheetTests/ReferenceHelperDVTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,38 @@ private function setDataValidation(Worksheet $sheet, string $cellAddress): void
130130
$validation->setPrompt('Please pick a value from the drop-down list.');
131131
$validation->setFormula1('$A$5:$A$8');
132132
}
133+
134+
public function testMultipleRanges(): void
135+
{
136+
$spreadsheet = new Spreadsheet();
137+
$sheet = $spreadsheet->getActiveSheet();
138+
$sheet->getCell('B1')->setValue(1);
139+
$sheet->getCell('B2')->setValue(2);
140+
$sheet->getCell('B3')->setValue(3);
141+
$dv = $sheet->getDataValidation('A1:A4 C5 D6:D7');
142+
$dv->setType(DataValidation::TYPE_LIST)
143+
->setShowDropDown(true)
144+
->setFormula1('$B$1:$B$3')
145+
->setErrorStyle(DataValidation::STYLE_STOP)
146+
->setShowErrorMessage(true)
147+
->setErrorTitle('Input Error')
148+
->setError('Value is not a member of allowed list');
149+
$sheet->insertNewColumnBefore('A');
150+
$dvs = $sheet->getDataValidationCollection();
151+
self::assertCount(1, $dvs);
152+
$expected = 'B1:B4 D5 E6:E7';
153+
self::assertSame([$expected], array_keys($dvs));
154+
$dv = $dvs[$expected];
155+
self::assertSame($expected, $dv->getSqref());
156+
self::assertSame('$C$1:$C$3', $dv->getFormula1());
157+
$sheet->getCell('B2')->setValue(3);
158+
self::assertTrue($sheet->getCell('B2')->hasValidValue());
159+
$sheet->getCell('D5')->setValue(7);
160+
self::assertFalse($sheet->getCell('D5')->hasValidValue());
161+
$sheet->getCell('E6')->setValue(7);
162+
self::assertFalse($sheet->getCell('E6')->hasValidValue());
163+
$sheet->getCell('E7')->setValue(1);
164+
self::assertTrue($sheet->getCell('E7')->hasValidValue());
165+
$spreadsheet->disconnectWorksheets();
166+
}
133167
}

tests/PhpSpreadsheetTests/Writer/Xlsx/Issue2368Test.php

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,7 @@ public function testMultipleRange(): void
4646
$spreadsheet = $reader->load($filename);
4747
$sheet = $spreadsheet->getActiveSheet();
4848
$validations = $sheet->getDataValidationCollection();
49-
/** @var string[] */
50-
$ranges = [];
51-
foreach ($validations as $validation) {
52-
$ranges[] = $validation->getSqref();
53-
}
54-
self::assertContains('A1:A5', $ranges);
55-
self::assertContains('A10:A14', $ranges);
56-
self::assertContains('A20:A24', $ranges);
49+
self::assertSame(['A1:A5 A10:A14 A20:A24'], array_keys($validations));
5750
self::assertSame('"yes,no"', $sheet->getCell('A3')->getDataValidation()->getFormula1());
5851
self::assertSame('"yes,no"', $sheet->getCell('A10')->getDataValidation()->getFormula1());
5952
self::assertSame('"yes,no"', $sheet->getCell('A24')->getDataValidation()->getFormula1());
@@ -63,14 +56,7 @@ public function testMultipleRange(): void
6356

6457
$sheet2 = $reloadedSpreadsheet->getActiveSheet();
6558
$validation2 = $sheet2->getDataValidationCollection();
66-
/** @var string[] */
67-
$range2 = [];
68-
foreach ($validation2 as $validation) {
69-
$range2[] = $validation->getSqref();
70-
}
71-
self::assertContains('A1:A5', $range2);
72-
self::assertContains('A10:A14', $range2);
73-
self::assertContains('A20:A24', $range2);
59+
self::assertSame(['A1:A5 A10:A14 A20:A24'], array_keys($validation2));
7460
self::assertSame('"yes,no"', $sheet2->getCell('A3')->getDataValidation()->getFormula1());
7561
self::assertSame('"yes,no"', $sheet2->getCell('A10')->getDataValidation()->getFormula1());
7662
self::assertSame('"yes,no"', $sheet2->getCell('A24')->getDataValidation()->getFormula1());

0 commit comments

Comments
 (0)