Skip to content

Commit

Permalink
Allow Multiple Ranges
Browse files Browse the repository at this point in the history
  • Loading branch information
oleibman committed Dec 29, 2024
1 parent 222bf51 commit f943e2d
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 51 deletions.
37 changes: 17 additions & 20 deletions src/PhpSpreadsheet/Reader/Xlsx/DataValidations.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,26 +37,23 @@ public function load(): void
foreach ($this->worksheetXml->dataValidations->dataValidation as $dataValidation) {
// Uppercase coordinate
$range = strtoupper((string) $dataValidation['sqref']);
$rangeSet = explode(' ', $range);
foreach ($rangeSet as $range) {
$docValidation = new DataValidation();
$docValidation->setType((string) $dataValidation['type']);
$docValidation->setErrorStyle((string) $dataValidation['errorStyle']);
$docValidation->setOperator((string) $dataValidation['operator']);
$docValidation->setAllowBlank(filter_var($dataValidation['allowBlank'], FILTER_VALIDATE_BOOLEAN));
// showDropDown is inverted (works as hideDropDown if true)
$docValidation->setShowDropDown(!filter_var($dataValidation['showDropDown'], FILTER_VALIDATE_BOOLEAN));
$docValidation->setShowInputMessage(filter_var($dataValidation['showInputMessage'], FILTER_VALIDATE_BOOLEAN));
$docValidation->setShowErrorMessage(filter_var($dataValidation['showErrorMessage'], FILTER_VALIDATE_BOOLEAN));
$docValidation->setErrorTitle((string) $dataValidation['errorTitle']);
$docValidation->setError((string) $dataValidation['error']);
$docValidation->setPromptTitle((string) $dataValidation['promptTitle']);
$docValidation->setPrompt((string) $dataValidation['prompt']);
$docValidation->setFormula1(Xlsx::replacePrefixes((string) $dataValidation->formula1));
$docValidation->setFormula2(Xlsx::replacePrefixes((string) $dataValidation->formula2));
$docValidation->setSqref($range);
$this->worksheet->setDataValidation($range, $docValidation);
}
$docValidation = new DataValidation();
$docValidation->setType((string) $dataValidation['type']);
$docValidation->setErrorStyle((string) $dataValidation['errorStyle']);
$docValidation->setOperator((string) $dataValidation['operator']);
$docValidation->setAllowBlank(filter_var($dataValidation['allowBlank'], FILTER_VALIDATE_BOOLEAN));
// showDropDown is inverted (works as hideDropDown if true)
$docValidation->setShowDropDown(!filter_var($dataValidation['showDropDown'], FILTER_VALIDATE_BOOLEAN));
$docValidation->setShowInputMessage(filter_var($dataValidation['showInputMessage'], FILTER_VALIDATE_BOOLEAN));
$docValidation->setShowErrorMessage(filter_var($dataValidation['showErrorMessage'], FILTER_VALIDATE_BOOLEAN));
$docValidation->setErrorTitle((string) $dataValidation['errorTitle']);
$docValidation->setError((string) $dataValidation['error']);
$docValidation->setPromptTitle((string) $dataValidation['promptTitle']);
$docValidation->setPrompt((string) $dataValidation['prompt']);
$docValidation->setFormula1(Xlsx::replacePrefixes((string) $dataValidation->formula1));
$docValidation->setFormula2(Xlsx::replacePrefixes((string) $dataValidation->formula2));
$docValidation->setSqref($range);
$this->worksheet->setDataValidation($range, $docValidation);
}
}
}
8 changes: 7 additions & 1 deletion src/PhpSpreadsheet/ReferenceHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,13 @@ protected function adjustDataValidations(Worksheet $worksheet, int $numberOfColu
)
);
}
$newReference = $this->updateCellReference($cellAddress);
$addressParts = explode(' ', $cellAddress);
$newReference = '';
$separator = '';
foreach ($addressParts as $addressPart) {
$newReference .= $separator . $this->updateCellReference($addressPart);
$separator = ' ';
}
if ($cellAddress !== $newReference) {
$dataValidation->setSqref($newReference);
$worksheet->setDataValidation($newReference, $dataValidation);
Expand Down
47 changes: 35 additions & 12 deletions src/PhpSpreadsheet/Worksheet/Worksheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ class Worksheet

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

Expand Down Expand Up @@ -3261,23 +3262,30 @@ public function getHyperlinkCollection(): array
*/
public function getDataValidation(string $cellCoordinate): DataValidation
{
// return data validation if we already have one
if (isset($this->dataValidationCollection[$cellCoordinate])) {
return $this->dataValidationCollection[$cellCoordinate];
}
foreach ($this->dataValidationCollection as $dataValidation) {
$sqref = $dataValidation->getSqref() ?? '';
if (str_contains($sqref, ':')) {
if (Coordinate::coordinateIsInsideRange($sqref, $cellCoordinate)) {

foreach ($this->dataValidationCollection as $key => $dataValidation) {
$keyParts = explode(' ', $key);
foreach ($keyParts as $keyPart) {
if ($keyPart === $cellCoordinate) {
return $dataValidation;
}
if (str_contains($keyPart, ':')) {
if (Coordinate::coordinateIsInsideRange($keyPart, $cellCoordinate)) {
return $dataValidation;
}
}
}
}

// else create data validation
$this->dataValidationCollection[$cellCoordinate] = new DataValidation();
$dataValidation = new DataValidation();
$dataValidation->setSqref($cellCoordinate);
$this->dataValidationCollection[$cellCoordinate] = $dataValidation;

return $this->dataValidationCollection[$cellCoordinate];
return $dataValidation;
}

/**
Expand Down Expand Up @@ -3308,12 +3316,17 @@ public function dataValidationExists(string $coordinate): bool
if (isset($this->dataValidationCollection[$coordinate])) {
return true;
}
foreach ($this->dataValidationCollection as $dataValidation) {
$sqref = $dataValidation->getSqref() ?? '';
if (str_contains($sqref, ':')) {
if (Coordinate::coordinateIsInsideRange($sqref, $coordinate)) {
foreach ($this->dataValidationCollection as $key => $dataValidation) {
$keyParts = explode(' ', $key);
foreach ($keyParts as $keyPart) {
if ($keyPart === $coordinate) {
return true;
}
if (str_contains($keyPart, ':')) {
if (Coordinate::coordinateIsInsideRange($keyPart, $coordinate)) {
return true;
}
}
}
}

Expand All @@ -3327,7 +3340,17 @@ public function dataValidationExists(string $coordinate): bool
*/
public function getDataValidationCollection(): array
{
return $this->dataValidationCollection;
$collectionCells = [];
$collectionRanges = [];
foreach ($this->dataValidationCollection as $key => $dataValidation) {
if (preg_match('/[: ]/', $key) === 1) {
$collectionRanges[$key] = $dataValidation;
} else {
$collectionCells[$key] = $dataValidation;
}
}

return array_merge($collectionCells, $collectionRanges);
}

/**
Expand Down
9 changes: 8 additions & 1 deletion src/PhpSpreadsheet/Writer/Xls/Worksheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -2605,7 +2605,14 @@ private function writeMsoDrawing(): void
private function writeDataValidity(): void
{
// Datavalidation collection
$dataValidationCollection = $this->phpSheet->getDataValidationCollection();
$dataValidationCollection1 = $this->phpSheet->getDataValidationCollection();
$dataValidationCollection = [];
foreach ($dataValidationCollection1 as $key => $dataValidation) {
$keyParts = explode(' ', $key);
foreach ($keyParts as $keyPart) {
$dataValidationCollection[$keyPart] = $dataValidation;
}
}

// Write data validations?
if (!empty($dataValidationCollection)) {
Expand Down
1 change: 0 additions & 1 deletion src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,6 @@ private function writeDataValidations(XMLWriter $objWriter, PhpspreadsheetWorksh

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

Expand Down
34 changes: 34 additions & 0 deletions tests/PhpSpreadsheetTests/ReferenceHelperDVTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,38 @@ private function setDataValidation(Worksheet $sheet, string $cellAddress): void
$validation->setPrompt('Please pick a value from the drop-down list.');
$validation->setFormula1('$A$5:$A$8');
}

public function testMultipleRanges(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->getCell('B1')->setValue(1);
$sheet->getCell('B2')->setValue(2);
$sheet->getCell('B3')->setValue(3);
$dv = $sheet->getDataValidation('A1:A4 C5 D6:D7');
$dv->setType(DataValidation::TYPE_LIST)
->setShowDropDown(true)
->setFormula1('$B$1:$B$3')
->setErrorStyle(DataValidation::STYLE_STOP)
->setShowErrorMessage(true)
->setErrorTitle('Input Error')
->setError('Value is not a member of allowed list');
$sheet->insertNewColumnBefore('A');
$dvs = $sheet->getDataValidationCollection();
self::assertCount(1, $dvs);
$expected = 'B1:B4 D5 E6:E7';
self::assertSame([$expected], array_keys($dvs));
$dv = $dvs[$expected];
self::assertSame($expected, $dv->getSqref());
self::assertSame('$C$1:$C$3', $dv->getFormula1());
$sheet->getCell('B2')->setValue(3);
self::assertTrue($sheet->getCell('B2')->hasValidValue());
$sheet->getCell('D5')->setValue(7);
self::assertFalse($sheet->getCell('D5')->hasValidValue());
$sheet->getCell('E6')->setValue(7);
self::assertFalse($sheet->getCell('E6')->hasValidValue());
$sheet->getCell('E7')->setValue(1);
self::assertTrue($sheet->getCell('E7')->hasValidValue());
$spreadsheet->disconnectWorksheets();
}
}
18 changes: 2 additions & 16 deletions tests/PhpSpreadsheetTests/Writer/Xlsx/Issue2368Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,7 @@ public function testMultipleRange(): void
$spreadsheet = $reader->load($filename);
$sheet = $spreadsheet->getActiveSheet();
$validations = $sheet->getDataValidationCollection();
/** @var string[] */
$ranges = [];
foreach ($validations as $validation) {
$ranges[] = $validation->getSqref();
}
self::assertContains('A1:A5', $ranges);
self::assertContains('A10:A14', $ranges);
self::assertContains('A20:A24', $ranges);
self::assertSame(['A1:A5 A10:A14 A20:A24'], array_keys($validations));
self::assertSame('"yes,no"', $sheet->getCell('A3')->getDataValidation()->getFormula1());
self::assertSame('"yes,no"', $sheet->getCell('A10')->getDataValidation()->getFormula1());
self::assertSame('"yes,no"', $sheet->getCell('A24')->getDataValidation()->getFormula1());
Expand All @@ -63,14 +56,7 @@ public function testMultipleRange(): void

$sheet2 = $reloadedSpreadsheet->getActiveSheet();
$validation2 = $sheet2->getDataValidationCollection();
/** @var string[] */
$range2 = [];
foreach ($validation2 as $validation) {
$range2[] = $validation->getSqref();
}
self::assertContains('A1:A5', $range2);
self::assertContains('A10:A14', $range2);
self::assertContains('A20:A24', $range2);
self::assertSame(['A1:A5 A10:A14 A20:A24'], array_keys($validation2));
self::assertSame('"yes,no"', $sheet2->getCell('A3')->getDataValidation()->getFormula1());
self::assertSame('"yes,no"', $sheet2->getCell('A10')->getDataValidation()->getFormula1());
self::assertSame('"yes,no"', $sheet2->getCell('A24')->getDataValidation()->getFormula1());
Expand Down

0 comments on commit f943e2d

Please sign in to comment.