Skip to content

Commit 6561494

Browse files
SailorMaxPowerKiKi
authored andcommitted
Add possibility to check validity of a cell value, based on data validation rules
`$cell->hasValidValue()` returns true if the cell has a value which conform to the rules defined in `$cell->getDataValidation()`. Closes #257
1 parent 888c618 commit 6561494

File tree

4 files changed

+165
-2
lines changed

4 files changed

+165
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
### Added
1111

1212
- Support for chart fill color - @CrazyBite [#158](https://github.com/PHPOffice/PhpSpreadsheet/pull/158)
13-
- Support for read Hyperlink for xml - [@GreatHumorist](https://github.com/GreatHumorist) [#223](https://github.com/PHPOffice/PhpSpreadsheet/pull/223)
13+
- Support for read Hyperlink for xml - @GreatHumorist [#223](https://github.com/PHPOffice/PhpSpreadsheet/pull/223)
14+
- Support for cell value validation according to data validation rules - @SailorMax [#257](https://github.com/PHPOffice/PhpSpreadsheet/pull/257)
1415

1516
### Changed
1617

src/PhpSpreadsheet/Cell/Cell.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,19 @@ public function setDataValidation(DataValidation $pDataValidation = null)
415415
}
416416

417417
/**
418-
* Does this cell contain a Hyperlink?
418+
* Does this cell contain valid value?
419+
*
420+
* @return bool
421+
*/
422+
public function hasValidValue()
423+
{
424+
$validator = new DataValidator();
425+
426+
return $validator->isValid($this);
427+
}
428+
429+
/**
430+
* Does this cell contain a Hyperlink?
419431
*
420432
* @throws Exception
421433
*
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheet\Cell;
4+
5+
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
6+
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
7+
use PhpOffice\PhpSpreadsheet\Exception;
8+
9+
/**
10+
* Validate a cell value according to its validation rules.
11+
*/
12+
class DataValidator
13+
{
14+
/**
15+
* Does this cell contain valid value?
16+
*
17+
* @param Cell $cell Cell to check the value
18+
*
19+
* @return bool
20+
*/
21+
public function isValid(Cell $cell)
22+
{
23+
if (!$cell->hasDataValidation()) {
24+
return true;
25+
}
26+
27+
$cellValue = $cell->getValue();
28+
$dataValidation = $cell->getDataValidation();
29+
30+
if (!$dataValidation->getAllowBlank() && ($cellValue === null || $cellValue === '')) {
31+
return false;
32+
}
33+
34+
// TODO: write check on all cases
35+
switch ($dataValidation->getType()) {
36+
case DataValidation::TYPE_LIST:
37+
return $this->isValueInList($cell);
38+
}
39+
40+
return false;
41+
}
42+
43+
/**
44+
* Does this cell contain valid value, based on list?
45+
*
46+
* @param Cell $cell Cell to check the value
47+
*
48+
* @return bool
49+
*/
50+
private function isValueInList(Cell $cell)
51+
{
52+
$cellValue = $cell->getValue();
53+
$dataValidation = $cell->getDataValidation();
54+
55+
$formula1 = $dataValidation->getFormula1();
56+
if (!empty($formula1)) {
57+
// inline values list
58+
if ($formula1[0] === '"') {
59+
return in_array(strtolower($cellValue), explode(',', strtolower(trim($formula1, '"'))), true);
60+
} elseif (strpos($formula1, ':') > 0) {
61+
// values list cells
62+
$matchFormula = '=MATCH(' . $cell->getCoordinate() . ', ' . $formula1 . ', 0)';
63+
$calculation = Calculation::getInstance($cell->getWorksheet()->getParent());
64+
65+
try {
66+
$result = $calculation->calculateFormula($matchFormula, $cell->getCoordinate(), $cell);
67+
68+
return $result !== Functions::NA();
69+
} catch (Exception $ex) {
70+
return false;
71+
}
72+
}
73+
}
74+
75+
return true;
76+
}
77+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
namespace PhpOffice\PhpSpreadsheetTests\Cell;
4+
5+
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
6+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
7+
use PHPUnit_Framework_TestCase;
8+
9+
class DataValidatorTest extends PHPUnit_Framework_TestCase
10+
{
11+
public function testNoValidation()
12+
{
13+
$spreadsheet = new Spreadsheet();
14+
$sheet = $spreadsheet->getActiveSheet();
15+
$testCell = $sheet->getCell('A1');
16+
17+
self::assertTrue($testCell->hasValidValue(), 'a cell without any validation data is always valid');
18+
}
19+
20+
public function testUnsupportedType()
21+
{
22+
$spreadsheet = new Spreadsheet();
23+
$sheet = $spreadsheet->getActiveSheet();
24+
$testCell = $sheet->getCell('A1');
25+
26+
$validation = $testCell->getDataValidation();
27+
$validation->setType(DataValidation::TYPE_CUSTOM);
28+
$validation->setAllowBlank(true);
29+
30+
self::assertFalse($testCell->hasValidValue(), 'cannot assert that value is valid when the validation type is not supported');
31+
}
32+
33+
public function testList()
34+
{
35+
$spreadsheet = new Spreadsheet();
36+
$sheet = $spreadsheet->getActiveSheet();
37+
$testCell = $sheet->getCell('A1');
38+
39+
$validation = $testCell->getDataValidation();
40+
$validation->setType(DataValidation::TYPE_LIST);
41+
42+
// blank value
43+
$testCell->setValue('');
44+
$validation->setAllowBlank(true);
45+
self::assertTrue($testCell->hasValidValue(), 'cell can be empty');
46+
$validation->setAllowBlank(false);
47+
self::assertFalse($testCell->hasValidValue(), 'cell can not be empty');
48+
49+
// inline list
50+
$validation->setFormula1('"yes,no"');
51+
$testCell->setValue('foo');
52+
self::assertFalse($testCell->hasValidValue(), "cell value ('foo') is not allowed");
53+
$testCell->setValue('yes');
54+
self::assertTrue($testCell->hasValidValue(), "cell value ('yes') has to be allowed");
55+
56+
// list from cells
57+
$sheet->getCell('B1')->setValue(5);
58+
$sheet->getCell('B2')->setValue(6);
59+
$sheet->getCell('B3')->setValue(7);
60+
$testCell = $sheet->getCell('A1'); // redefine $testCell, because it has broken coordinates after using other cells
61+
$validation->setFormula1('B1:B3');
62+
$testCell->setValue('10');
63+
self::assertFalse($testCell->hasValidValue(), "cell value ('10') is not allowed");
64+
$testCell = $sheet->getCell('A1'); // redefine $testCell, because it has broken coordinates after using other cells
65+
$testCell->setValue('5');
66+
self::assertTrue($testCell->hasValidValue(), "cell value ('5') has to be allowed");
67+
68+
$testCell = $sheet->getCell('A1'); // redefine $testCell, because it has broken coordinates after using other cells
69+
$validation->setFormula1('broken : cell : coordinates');
70+
71+
self::assertFalse($testCell->hasValidValue(), 'invalid formula should not throw exceptions');
72+
}
73+
}

0 commit comments

Comments
 (0)