Merge branch 'master' into Xls-Reader-Conditional-Formatting
This commit is contained in:
commit
23e2d702ff
|
|
@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).
|
|||
|
||||
### Added
|
||||
|
||||
- Implementation of the ISREF() information function.
|
||||
- Implementation of the UNIQUE() Lookup/Reference (array) function
|
||||
- Implementation of the ISREF() Information function.
|
||||
- Added support for reading "formatted" numeric values from Csv files; although default behaviour of reading these values as strings is preserved.
|
||||
|
||||
(i.e a value of "12,345.67" can be read as numeric `1235.67`, not simply as a string `"12,345.67"`, if the `castFormattedNumberToNumeric()` setting is enabled.
|
||||
|
|
@ -31,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
|
|||
This is determined by the Calculation Engine locale setting.
|
||||
|
||||
(i.e. `"Vrai"` wil be converted to a boolean `true` if the Locale is set to `fr`.)
|
||||
- Allow `psr/simple-cache` 2.x
|
||||
|
||||
### Deprecated
|
||||
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
"markbaker/matrix": "^3.0",
|
||||
"psr/http-client": "^1.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/simple-cache": "^1.0"
|
||||
"psr/simple-cache": "^1.0 || ^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "dev-master",
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "9dadb265f548dd4ddbfd2ee97d5a6aa6",
|
||||
"content-hash": "ed42c40a4281d97171980367f24d0a25",
|
||||
"packages": [
|
||||
{
|
||||
"name": "ezyang/htmlpurifier",
|
||||
|
|
|
|||
|
|
@ -2300,16 +2300,6 @@ parameters:
|
|||
count: 2
|
||||
path: src/PhpSpreadsheet/Reader/Xls.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$errorStyle of method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\DataValidation\\:\\:setErrorStyle\\(\\) expects string, int\\|string given\\.$#"
|
||||
count: 1
|
||||
path: src/PhpSpreadsheet/Reader/Xls.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$operator of method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\DataValidation\\:\\:setOperator\\(\\) expects string, int\\|string given\\.$#"
|
||||
count: 1
|
||||
path: src/PhpSpreadsheet/Reader/Xls.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$showSummaryBelow of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\Worksheet\\:\\:setShowSummaryBelow\\(\\) expects bool, int given\\.$#"
|
||||
count: 1
|
||||
|
|
@ -2320,11 +2310,6 @@ parameters:
|
|||
count: 1
|
||||
path: src/PhpSpreadsheet/Reader/Xls.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$type of method PhpOffice\\\\PhpSpreadsheet\\\\Cell\\\\DataValidation\\:\\:setType\\(\\) expects string, int\\|string given\\.$#"
|
||||
count: 1
|
||||
path: src/PhpSpreadsheet/Reader/Xls.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#2 \\$row of method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\IReadFilter\\:\\:readCell\\(\\) expects int, string given\\.$#"
|
||||
count: 1
|
||||
|
|
@ -5484,3 +5469,4 @@ parameters:
|
|||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Writer\\\\Xlsx\\\\Xlfn\\:\\:addXlfn\\(\\) should return string but returns string\\|null\\.$#"
|
||||
count: 1
|
||||
path: src/PhpSpreadsheet/Writer/Xlsx/Xlfn.php
|
||||
|
||||
|
|
|
|||
|
|
@ -2583,7 +2583,7 @@ class Calculation
|
|||
],
|
||||
'UNIQUE' => [
|
||||
'category' => Category::CATEGORY_LOOKUP_AND_REFERENCE,
|
||||
'functionCall' => [Functions::class, 'DUMMY'],
|
||||
'functionCall' => [LookupRef\Unique::class, 'unique'],
|
||||
'argumentCount' => '1+',
|
||||
],
|
||||
'UPPER' => [
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class ErrorValue
|
|||
return false;
|
||||
}
|
||||
|
||||
return in_array($value, ExcelError::$errorCodes);
|
||||
return in_array($value, ExcelError::$errorCodes) || $value === ExcelError::CALC();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ class ExcelError
|
|||
'num' => '#NUM!',
|
||||
'na' => '#N/A',
|
||||
'gettingdata' => '#GETTING_DATA',
|
||||
'spill' => '#SPILL!',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -45,6 +46,10 @@ class ExcelError
|
|||
++$i;
|
||||
}
|
||||
|
||||
if ($value === self::CALC()) {
|
||||
return 14;
|
||||
}
|
||||
|
||||
return self::NA();
|
||||
}
|
||||
|
||||
|
|
@ -127,10 +132,20 @@ class ExcelError
|
|||
/**
|
||||
* DIV0.
|
||||
*
|
||||
* @return string #Not Yet Implemented
|
||||
* @return string #DIV/0!
|
||||
*/
|
||||
public static function DIV0()
|
||||
{
|
||||
return self::$errorCodes['divisionbyzero'];
|
||||
}
|
||||
|
||||
/**
|
||||
* CALC.
|
||||
*
|
||||
* @return string #Not Yet Implemented
|
||||
*/
|
||||
public static function CALC()
|
||||
{
|
||||
return '#CALC!';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class Unique
|
||||
{
|
||||
/**
|
||||
* UNIQUE
|
||||
* The UNIQUE function searches for value either from a one-row or one-column range or from an array.
|
||||
*
|
||||
* @param mixed $lookupVector The range of cells being searched
|
||||
* @param mixed $byColumn Whether the uniqueness should be determined by row (the default) or by column
|
||||
* @param mixed $exactlyOnce Whether the function should return only entries that occur just once in the list
|
||||
*
|
||||
* @return mixed The unique values from the search range
|
||||
*/
|
||||
public static function unique($lookupVector, $byColumn = false, $exactlyOnce = false)
|
||||
{
|
||||
if (!is_array($lookupVector)) {
|
||||
// Scalars are always returned "as is"
|
||||
return $lookupVector;
|
||||
}
|
||||
|
||||
$byColumn = (bool) $byColumn;
|
||||
$exactlyOnce = (bool) $exactlyOnce;
|
||||
|
||||
return ($byColumn === true)
|
||||
? self::uniqueByColumn($lookupVector, $exactlyOnce)
|
||||
: self::uniqueByRow($lookupVector, $exactlyOnce);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
private static function uniqueByRow(array $lookupVector, bool $exactlyOnce)
|
||||
{
|
||||
// When not $byColumn, we count whole rows or values, not individual values
|
||||
// so implode each row into a single string value
|
||||
array_walk(
|
||||
$lookupVector,
|
||||
function (array &$value): void {
|
||||
$value = implode(chr(0x00), $value);
|
||||
}
|
||||
);
|
||||
|
||||
$result = self::countValuesCaseInsensitive($lookupVector);
|
||||
|
||||
if ($exactlyOnce === true) {
|
||||
$result = self::exactlyOnceFilter($result);
|
||||
}
|
||||
|
||||
if (count($result) === 0) {
|
||||
return ExcelError::CALC();
|
||||
}
|
||||
|
||||
$result = array_keys($result);
|
||||
|
||||
// restore rows from their strings
|
||||
array_walk(
|
||||
$result,
|
||||
function (string &$value): void {
|
||||
$value = explode(chr(0x00), $value);
|
||||
}
|
||||
);
|
||||
|
||||
return (count($result) === 1) ? array_pop($result) : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
private static function uniqueByColumn(array $lookupVector, bool $exactlyOnce)
|
||||
{
|
||||
$flattenedLookupVector = Functions::flattenArray($lookupVector);
|
||||
|
||||
if (count($lookupVector, COUNT_RECURSIVE) > count($flattenedLookupVector, COUNT_RECURSIVE) + 1) {
|
||||
// We're looking at a full column check (multiple rows)
|
||||
$transpose = Matrix::transpose($lookupVector);
|
||||
$result = self::uniqueByRow($transpose, $exactlyOnce);
|
||||
|
||||
return (is_array($result)) ? Matrix::transpose($result) : $result;
|
||||
}
|
||||
|
||||
$result = self::countValuesCaseInsensitive($flattenedLookupVector);
|
||||
|
||||
if ($exactlyOnce === true) {
|
||||
$result = self::exactlyOnceFilter($result);
|
||||
}
|
||||
|
||||
if (count($result) === 0) {
|
||||
return ExcelError::CALC();
|
||||
}
|
||||
|
||||
$result = array_keys($result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private static function countValuesCaseInsensitive(array $caseSensitiveLookupValues): array
|
||||
{
|
||||
$caseInsensitiveCounts = array_count_values(
|
||||
array_map(
|
||||
function (string $value) {
|
||||
return StringHelper::strToUpper($value);
|
||||
},
|
||||
$caseSensitiveLookupValues
|
||||
)
|
||||
);
|
||||
|
||||
$caseSensitiveCounts = [];
|
||||
foreach ($caseInsensitiveCounts as $caseInsensitiveKey => $count) {
|
||||
if (is_numeric($caseInsensitiveKey)) {
|
||||
$caseSensitiveCounts[$caseInsensitiveKey] = $count;
|
||||
} else {
|
||||
foreach ($caseSensitiveLookupValues as $caseSensitiveValue) {
|
||||
if ($caseInsensitiveKey === StringHelper::strToUpper($caseSensitiveValue)) {
|
||||
$caseSensitiveCounts[$caseSensitiveValue] = $count;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $caseSensitiveCounts;
|
||||
}
|
||||
|
||||
private static function exactlyOnceFilter(array $values): array
|
||||
{
|
||||
return array_filter(
|
||||
$values,
|
||||
function ($value) {
|
||||
return $value === 1;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -4788,57 +4788,11 @@ class Xls extends BaseReader
|
|||
|
||||
// bit: 0-3; mask: 0x0000000F; type
|
||||
$type = (0x0000000F & $options) >> 0;
|
||||
switch ($type) {
|
||||
case 0x00:
|
||||
$type = DataValidation::TYPE_NONE;
|
||||
|
||||
break;
|
||||
case 0x01:
|
||||
$type = DataValidation::TYPE_WHOLE;
|
||||
|
||||
break;
|
||||
case 0x02:
|
||||
$type = DataValidation::TYPE_DECIMAL;
|
||||
|
||||
break;
|
||||
case 0x03:
|
||||
$type = DataValidation::TYPE_LIST;
|
||||
|
||||
break;
|
||||
case 0x04:
|
||||
$type = DataValidation::TYPE_DATE;
|
||||
|
||||
break;
|
||||
case 0x05:
|
||||
$type = DataValidation::TYPE_TIME;
|
||||
|
||||
break;
|
||||
case 0x06:
|
||||
$type = DataValidation::TYPE_TEXTLENGTH;
|
||||
|
||||
break;
|
||||
case 0x07:
|
||||
$type = DataValidation::TYPE_CUSTOM;
|
||||
|
||||
break;
|
||||
}
|
||||
$type = Xls\DataValidationHelper::type($type);
|
||||
|
||||
// bit: 4-6; mask: 0x00000070; error type
|
||||
$errorStyle = (0x00000070 & $options) >> 4;
|
||||
switch ($errorStyle) {
|
||||
case 0x00:
|
||||
$errorStyle = DataValidation::STYLE_STOP;
|
||||
|
||||
break;
|
||||
case 0x01:
|
||||
$errorStyle = DataValidation::STYLE_WARNING;
|
||||
|
||||
break;
|
||||
case 0x02:
|
||||
$errorStyle = DataValidation::STYLE_INFORMATION;
|
||||
|
||||
break;
|
||||
}
|
||||
$errorStyle = Xls\DataValidationHelper::errorStyle($errorStyle);
|
||||
|
||||
// bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
|
||||
// I have only seen cases where this is 1
|
||||
|
|
@ -4858,39 +4812,10 @@ class Xls extends BaseReader
|
|||
|
||||
// bit: 20-23; mask: 0x00F00000; condition operator
|
||||
$operator = (0x00F00000 & $options) >> 20;
|
||||
switch ($operator) {
|
||||
case 0x00:
|
||||
$operator = DataValidation::OPERATOR_BETWEEN;
|
||||
$operator = Xls\DataValidationHelper::operator($operator);
|
||||
|
||||
break;
|
||||
case 0x01:
|
||||
$operator = DataValidation::OPERATOR_NOTBETWEEN;
|
||||
|
||||
break;
|
||||
case 0x02:
|
||||
$operator = DataValidation::OPERATOR_EQUAL;
|
||||
|
||||
break;
|
||||
case 0x03:
|
||||
$operator = DataValidation::OPERATOR_NOTEQUAL;
|
||||
|
||||
break;
|
||||
case 0x04:
|
||||
$operator = DataValidation::OPERATOR_GREATERTHAN;
|
||||
|
||||
break;
|
||||
case 0x05:
|
||||
$operator = DataValidation::OPERATOR_LESSTHAN;
|
||||
|
||||
break;
|
||||
case 0x06:
|
||||
$operator = DataValidation::OPERATOR_GREATERTHANOREQUAL;
|
||||
|
||||
break;
|
||||
case 0x07:
|
||||
$operator = DataValidation::OPERATOR_LESSTHANOREQUAL;
|
||||
|
||||
break;
|
||||
if ($type === null || $errorStyle === null || $operator === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// offset: 4; size: var; title of the prompt box
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
|
||||
|
||||
class DataValidationHelper
|
||||
{
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private static $types = [
|
||||
0x00 => DataValidation::TYPE_NONE,
|
||||
0x01 => DataValidation::TYPE_WHOLE,
|
||||
0x02 => DataValidation::TYPE_DECIMAL,
|
||||
0x03 => DataValidation::TYPE_LIST,
|
||||
0x04 => DataValidation::TYPE_DATE,
|
||||
0x05 => DataValidation::TYPE_TIME,
|
||||
0x06 => DataValidation::TYPE_TEXTLENGTH,
|
||||
0x07 => DataValidation::TYPE_CUSTOM,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private static $errorStyles = [
|
||||
0x00 => DataValidation::STYLE_STOP,
|
||||
0x01 => DataValidation::STYLE_WARNING,
|
||||
0x02 => DataValidation::STYLE_INFORMATION,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
*/
|
||||
private static $operators = [
|
||||
0x00 => DataValidation::OPERATOR_BETWEEN,
|
||||
0x01 => DataValidation::OPERATOR_NOTBETWEEN,
|
||||
0x02 => DataValidation::OPERATOR_EQUAL,
|
||||
0x03 => DataValidation::OPERATOR_NOTEQUAL,
|
||||
0x04 => DataValidation::OPERATOR_GREATERTHAN,
|
||||
0x05 => DataValidation::OPERATOR_LESSTHAN,
|
||||
0x06 => DataValidation::OPERATOR_GREATERTHANOREQUAL,
|
||||
0x07 => DataValidation::OPERATOR_LESSTHANOREQUAL,
|
||||
];
|
||||
|
||||
public static function type(int $type): ?string
|
||||
{
|
||||
if (isset(self::$types[$type])) {
|
||||
return self::$types[$type];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function errorStyle(int $errorStyle): ?string
|
||||
{
|
||||
if (isset(self::$errorStyles[$errorStyle])) {
|
||||
return self::$errorStyles[$errorStyle];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function operator(int $operator): ?string
|
||||
{
|
||||
if (isset(self::$operators[$operator])) {
|
||||
return self::$operators[$operator];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class UniqueTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider uniqueTestProvider
|
||||
*/
|
||||
public function testUnique(array $expectedResult, array $lookupRef, bool $byColumn = false, bool $exactlyOnce = false): void
|
||||
{
|
||||
$result = LookupRef\Unique::unique($lookupRef, $byColumn, $exactlyOnce);
|
||||
self::assertEquals($expectedResult, $result);
|
||||
}
|
||||
|
||||
public function testUniqueException(): void
|
||||
{
|
||||
$rowLookupData = [
|
||||
['Andrew', 'Brown'],
|
||||
['Betty', 'Johnson'],
|
||||
['Betty', 'Johnson'],
|
||||
['Andrew', 'Brown'],
|
||||
['David', 'White'],
|
||||
['Andrew', 'Brown'],
|
||||
['David', 'White'],
|
||||
];
|
||||
|
||||
$columnLookupData = [
|
||||
['PHP', 'Rocks', 'php', 'rocks'],
|
||||
];
|
||||
|
||||
$result = LookupRef\Unique::unique($rowLookupData, false, true);
|
||||
self::assertEquals(ExcelError::CALC(), $result);
|
||||
|
||||
$result = LookupRef\Unique::unique($columnLookupData, true, true);
|
||||
self::assertEquals(ExcelError::CALC(), $result);
|
||||
}
|
||||
|
||||
public function testUniqueWithScalar(): void
|
||||
{
|
||||
$lookupData = 123;
|
||||
|
||||
$result = LookupRef\Unique::unique($lookupData);
|
||||
self::assertSame($lookupData, $result);
|
||||
}
|
||||
|
||||
public function uniqueTestProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
[['Red'], ['Green'], ['Blue'], ['Orange']],
|
||||
[
|
||||
['Red'],
|
||||
['Green'],
|
||||
['Green'],
|
||||
['Blue'],
|
||||
['Blue'],
|
||||
['Orange'],
|
||||
['Green'],
|
||||
['Blue'],
|
||||
['Red'],
|
||||
],
|
||||
],
|
||||
[
|
||||
[['Red'], ['Green'], ['Blue'], ['Orange']],
|
||||
[
|
||||
['Red'],
|
||||
['Green'],
|
||||
['GrEEn'],
|
||||
['Blue'],
|
||||
['BLUE'],
|
||||
['Orange'],
|
||||
['GReeN'],
|
||||
['blue'],
|
||||
['RED'],
|
||||
],
|
||||
],
|
||||
[
|
||||
['Orange'],
|
||||
[
|
||||
['Red'],
|
||||
['Green'],
|
||||
['Green'],
|
||||
['Blue'],
|
||||
['Blue'],
|
||||
['Orange'],
|
||||
['Green'],
|
||||
['Blue'],
|
||||
['Red'],
|
||||
],
|
||||
false,
|
||||
true,
|
||||
],
|
||||
[
|
||||
['Andrew', 'Betty', 'Robert', 'David'],
|
||||
[['Andrew', 'Betty', 'Robert', 'Andrew', 'Betty', 'Robert', 'David', 'Andrew']],
|
||||
true,
|
||||
],
|
||||
[
|
||||
['David'],
|
||||
[['Andrew', 'Betty', 'Robert', 'Andrew', 'Betty', 'Robert', 'David', 'Andrew']],
|
||||
true,
|
||||
true,
|
||||
],
|
||||
[
|
||||
[1, 1, 2, 2, 3],
|
||||
[[1, 1, 2, 2, 3]],
|
||||
],
|
||||
[
|
||||
[1, 2, 3],
|
||||
[[1, 1, 2, 2, 3]],
|
||||
true,
|
||||
],
|
||||
[
|
||||
[
|
||||
[1, 1, 2, 3],
|
||||
[1, 2, 2, 3],
|
||||
],
|
||||
[
|
||||
[1, 1, 2, 2, 3],
|
||||
[1, 2, 2, 2, 3],
|
||||
],
|
||||
true,
|
||||
],
|
||||
[
|
||||
[
|
||||
['Andrew', 'Brown'],
|
||||
['Betty', 'Johnson'],
|
||||
['David', 'White'],
|
||||
],
|
||||
[
|
||||
['Andrew', 'Brown'],
|
||||
['Betty', 'Johnson'],
|
||||
['Betty', 'Johnson'],
|
||||
['Andrew', 'Brown'],
|
||||
['David', 'White'],
|
||||
['Andrew', 'Brown'],
|
||||
['David', 'White'],
|
||||
],
|
||||
],
|
||||
[
|
||||
[[1.2], [2.1], [2.2], [3.0]],
|
||||
[
|
||||
[1.2],
|
||||
[1.2],
|
||||
[2.1],
|
||||
[2.2],
|
||||
[3.0],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xls;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xls;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class DataValidationTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var Worksheet
|
||||
*/
|
||||
protected $sheet;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$filename = 'tests/data/Reader/XLS/DataValidation.xls';
|
||||
$reader = new Xls();
|
||||
$spreadsheet = $reader->load($filename);
|
||||
$this->sheet = $spreadsheet->getActiveSheet();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataValidationProvider
|
||||
*/
|
||||
public function testDataValidation(string $expectedRange, array $expectedRule): void
|
||||
{
|
||||
$hasDataValidation = $this->sheet->dataValidationExists($expectedRange);
|
||||
self::assertTrue($hasDataValidation);
|
||||
|
||||
$dataValidation = $this->sheet->getDataValidation($expectedRange);
|
||||
self::assertSame($expectedRule['type'], $dataValidation->getType());
|
||||
self::assertSame($expectedRule['operator'], $dataValidation->getOperator());
|
||||
self::assertSame($expectedRule['formula'], $dataValidation->getFormula1());
|
||||
}
|
||||
|
||||
public function dataValidationProvider(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'B2',
|
||||
[
|
||||
'type' => DataValidation::TYPE_WHOLE,
|
||||
'operator' => DataValidation::OPERATOR_GREATERTHANOREQUAL,
|
||||
'formula' => '18',
|
||||
],
|
||||
],
|
||||
[
|
||||
'B3',
|
||||
[
|
||||
'type' => DataValidation::TYPE_LIST,
|
||||
'operator' => DataValidation::OPERATOR_BETWEEN,
|
||||
'formula' => '"Blocked,Pending,Approved"',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -298,6 +298,83 @@ class ReferenceHelperTest extends TestCase
|
|||
self::assertSame(['A3' => 'https://phpspreadsheet.readthedocs.io/en/latest/'], $hyperlinks);
|
||||
}
|
||||
|
||||
public function testInsertRowsWithDataValidation(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
$sheet->fromArray([['First'], ['Second'], ['Third'], ['Fourth']], null, 'A5', true);
|
||||
$cellAddress = 'E5';
|
||||
$this->setDataValidation($sheet, $cellAddress);
|
||||
|
||||
$sheet->insertNewRowBefore(2, 2);
|
||||
|
||||
self::assertFalse($sheet->getCell($cellAddress)->hasDataValidation());
|
||||
self::assertTrue($sheet->getCell('E7')->hasDataValidation());
|
||||
}
|
||||
|
||||
public function testDeleteRowsWithDataValidation(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
$sheet->fromArray([['First'], ['Second'], ['Third'], ['Fourth']], null, 'A5', true);
|
||||
$cellAddress = 'E5';
|
||||
$this->setDataValidation($sheet, $cellAddress);
|
||||
|
||||
$sheet->removeRow(2, 2);
|
||||
|
||||
self::assertFalse($sheet->getCell($cellAddress)->hasDataValidation());
|
||||
self::assertTrue($sheet->getCell('E3')->hasDataValidation());
|
||||
}
|
||||
|
||||
public function testDeleteColumnsWithDataValidation(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
$sheet->fromArray([['First'], ['Second'], ['Third'], ['Fourth']], null, 'A5', true);
|
||||
$cellAddress = 'E5';
|
||||
$this->setDataValidation($sheet, $cellAddress);
|
||||
|
||||
$sheet->removeColumn('B', 2);
|
||||
|
||||
self::assertFalse($sheet->getCell($cellAddress)->hasDataValidation());
|
||||
self::assertTrue($sheet->getCell('C5')->hasDataValidation());
|
||||
}
|
||||
|
||||
public function testInsertColumnsWithDataValidation(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
$sheet->fromArray([['First'], ['Second'], ['Third'], ['Fourth']], null, 'A5', true);
|
||||
$cellAddress = 'E5';
|
||||
$this->setDataValidation($sheet, $cellAddress);
|
||||
|
||||
$sheet->insertNewColumnBefore('C', 2);
|
||||
|
||||
self::assertFalse($sheet->getCell($cellAddress)->hasDataValidation());
|
||||
self::assertTrue($sheet->getCell('G5')->hasDataValidation());
|
||||
}
|
||||
|
||||
private function setDataValidation(Worksheet $sheet, string $cellAddress): void
|
||||
{
|
||||
$validation = $sheet->getCell($cellAddress)
|
||||
->getDataValidation();
|
||||
$validation->setType(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::TYPE_LIST);
|
||||
$validation->setErrorStyle(\PhpOffice\PhpSpreadsheet\Cell\DataValidation::STYLE_INFORMATION);
|
||||
$validation->setAllowBlank(false);
|
||||
$validation->setShowInputMessage(true);
|
||||
$validation->setShowErrorMessage(true);
|
||||
$validation->setShowDropDown(true);
|
||||
$validation->setErrorTitle('Input error');
|
||||
$validation->setError('Value is not in list.');
|
||||
$validation->setPromptTitle('Pick from list');
|
||||
$validation->setPrompt('Please pick a value from the drop-down list.');
|
||||
$validation->setFormula1('$A5:$A8');
|
||||
}
|
||||
|
||||
public function testInsertRowsWithConditionalFormatting(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
|
|
@ -306,6 +383,92 @@ class ReferenceHelperTest extends TestCase
|
|||
$sheet->getCell('H5')->setValue(5);
|
||||
|
||||
$cellRange = 'C3:F7';
|
||||
$this->setConditionalFormatting($sheet, $cellRange);
|
||||
|
||||
$sheet->insertNewRowBefore(4, 2);
|
||||
|
||||
$styles = $sheet->getConditionalStylesCollection();
|
||||
// verify that the conditional range has been updated
|
||||
self::assertSame('C3:F9', array_keys($styles)[0]);
|
||||
// verify that the conditions have been updated
|
||||
foreach ($styles as $style) {
|
||||
foreach ($style as $conditions) {
|
||||
self::assertSame('$H$7', $conditions->getConditions()[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testInsertColumnssWithConditionalFormatting(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->fromArray([[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8], [7, 8, 9, 10], [9, 10, 11, 12]], null, 'C3', true);
|
||||
$sheet->getCell('H5')->setValue(5);
|
||||
|
||||
$cellRange = 'C3:F7';
|
||||
$this->setConditionalFormatting($sheet, $cellRange);
|
||||
|
||||
$sheet->insertNewColumnBefore('C', 2);
|
||||
|
||||
$styles = $sheet->getConditionalStylesCollection();
|
||||
// verify that the conditional range has been updated
|
||||
self::assertSame('E3:H7', array_keys($styles)[0]);
|
||||
// verify that the conditions have been updated
|
||||
foreach ($styles as $style) {
|
||||
foreach ($style as $conditions) {
|
||||
self::assertSame('$J$5', $conditions->getConditions()[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testDeleteRowsWithConditionalFormatting(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->fromArray([[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8], [7, 8, 9, 10], [9, 10, 11, 12]], null, 'C3', true);
|
||||
$sheet->getCell('H5')->setValue(5);
|
||||
|
||||
$cellRange = 'C3:F7';
|
||||
$this->setConditionalFormatting($sheet, $cellRange);
|
||||
|
||||
$sheet->removeRow(4, 2);
|
||||
|
||||
$styles = $sheet->getConditionalStylesCollection();
|
||||
// verify that the conditional range has been updated
|
||||
self::assertSame('C3:F5', array_keys($styles)[0]);
|
||||
// verify that the conditions have been updated
|
||||
foreach ($styles as $style) {
|
||||
foreach ($style as $conditions) {
|
||||
self::assertSame('$H$5', $conditions->getConditions()[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testDeleteColumnsWithConditionalFormatting(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->fromArray([[1, 2, 3, 4], [3, 4, 5, 6], [5, 6, 7, 8], [7, 8, 9, 10], [9, 10, 11, 12]], null, 'C3', true);
|
||||
$sheet->getCell('H5')->setValue(5);
|
||||
|
||||
$cellRange = 'C3:F7';
|
||||
$this->setConditionalFormatting($sheet, $cellRange);
|
||||
|
||||
$sheet->removeColumn('D', 2);
|
||||
|
||||
$styles = $sheet->getConditionalStylesCollection();
|
||||
// verify that the conditional range has been updated
|
||||
self::assertSame('C3:D7', array_keys($styles)[0]);
|
||||
// verify that the conditions have been updated
|
||||
foreach ($styles as $style) {
|
||||
foreach ($style as $conditions) {
|
||||
self::assertSame('$F$5', $conditions->getConditions()[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function setConditionalFormatting(Worksheet $sheet, string $cellRange): void
|
||||
{
|
||||
$conditionalStyles = [];
|
||||
$wizardFactory = new Wizard($cellRange);
|
||||
/** @var Wizard\CellValue $cellWizard */
|
||||
|
|
@ -320,19 +483,55 @@ class ReferenceHelperTest extends TestCase
|
|||
$cellWizard->lessThan('$H$5', Wizard::VALUE_TYPE_CELL);
|
||||
$conditionalStyles[] = $cellWizard->getConditional();
|
||||
|
||||
$spreadsheet->getActiveSheet()
|
||||
->getStyle($cellWizard->getCellRange())
|
||||
$sheet->getStyle($cellWizard->getCellRange())
|
||||
->setConditionalStyles($conditionalStyles);
|
||||
$sheet->insertNewRowBefore(4, 2);
|
||||
}
|
||||
|
||||
$styles = $sheet->getConditionalStylesCollection();
|
||||
// verify that the conditional range has been updated
|
||||
self::assertSame('C3:F9', array_keys($styles)[0]);
|
||||
// verify that the conditions have been updated
|
||||
foreach ($styles as $style) {
|
||||
foreach ($style as $conditions) {
|
||||
self::assertSame('$H$7', $conditions->getConditions()[0]);
|
||||
}
|
||||
}
|
||||
public function testInsertRowsWithPrintArea(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->getPageSetup()->setPrintArea('A1:J10');
|
||||
|
||||
$sheet->insertNewRowBefore(2, 2);
|
||||
|
||||
$printArea = $sheet->getPageSetup()->getPrintArea();
|
||||
self::assertSame('A1:J12', $printArea);
|
||||
}
|
||||
|
||||
public function testInsertColumnsWithPrintArea(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->getPageSetup()->setPrintArea('A1:J10');
|
||||
|
||||
$sheet->insertNewColumnBefore('B', 2);
|
||||
|
||||
$printArea = $sheet->getPageSetup()->getPrintArea();
|
||||
self::assertSame('A1:L10', $printArea);
|
||||
}
|
||||
|
||||
public function testDeleteRowsWithPrintArea(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->getPageSetup()->setPrintArea('A1:J10');
|
||||
|
||||
$sheet->removeRow(2, 2);
|
||||
|
||||
$printArea = $sheet->getPageSetup()->getPrintArea();
|
||||
self::assertSame('A1:J8', $printArea);
|
||||
}
|
||||
|
||||
public function testDeleteColumnsWithPrintArea(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->getPageSetup()->setPrintArea('A1:J10');
|
||||
|
||||
$sheet->removeColumn('B', 2);
|
||||
|
||||
$printArea = $sheet->getPageSetup()->getPrintArea();
|
||||
self::assertSame('A1:H10', $printArea);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,4 +53,12 @@ return [
|
|||
7,
|
||||
'#N/A',
|
||||
],
|
||||
[
|
||||
9,
|
||||
'#SPILL!',
|
||||
],
|
||||
[
|
||||
14,
|
||||
'#CALC!',
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -49,6 +49,14 @@ return [
|
|||
true,
|
||||
'#N/A',
|
||||
],
|
||||
[
|
||||
true,
|
||||
'#SPILL!',
|
||||
],
|
||||
[
|
||||
true,
|
||||
'#CALC!',
|
||||
],
|
||||
[
|
||||
false,
|
||||
'TRUE',
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in New Issue