AutoFilter Improvements (#2393)

* AutoFilter Improvements

Fix issue #2378. The following changes are made:
- NotEqual tests must be part of a custom filter. Documentation has been changed to indicate that.
- Method setAndOr was replaced by setJoin some time ago. Documentation now reflects that change.
- Documentation to indicate that string filters are not case-sensitive, same as in Excel.
- Filters testing against numeric value now include a numeric test (not numeric for not equal, numeric for all others).
- String filter had previously treated everything as a test for "equal". It now handles "not equal" and the variants of "greater/less" with or without "equal".
- Documentation correctly stated that no more than 2 rules are allowed in a custom filter. Code did not enforce this restriction. It now does, throwing an exception if an attempt is made to add a third rule.
- Deleted a lot of comments in Rule.php to make it easier to see what is not yet implemented (between, begins with, etc.). I may take these on in future.
- Added a number of tests for the new functionality.

* Not Sure Why Phpstan Results Differ Local vs Github

Let's see if this change suffices.

* Phpstan Still

Not sure how to convince it. Let's try this.

* Phpstan Solved

Figured out the problem on my local machine. Expect this to work.
This commit is contained in:
oleibman 2021-11-16 07:46:07 -08:00 committed by GitHub
parent 52585a9d7c
commit 2a12587f05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 392 additions and 76 deletions

View File

@ -99,6 +99,8 @@ results are unpredictable.
Other filter expression types (such as cell colour filters) are not yet Other filter expression types (such as cell colour filters) are not yet
supported. supported.
String comparisons in filters are case-insensitive.
### Simple filters ### Simple filters
In MS Excel, Simple Filters are a dropdown list of all values used in In MS Excel, Simple Filters are a dropdown list of all values used in
@ -113,6 +115,8 @@ will be hidden.
To create a filter expression, we need to start by identifying the To create a filter expression, we need to start by identifying the
filter type. In this case, we're just going to specify that this filter filter type. In this case, we're just going to specify that this filter
is a standard filter. is a standard filter.
*Please note that Excel regards only tests for equal as a standard filter;
all others, including tests for not equal, must be supplied as custom filters.*
```php ```php
$columnFilter->setFilterType( $columnFilter->setFilterType(
@ -255,6 +259,7 @@ MS Excel uses `*` as a wildcard to match any number of characters, and `?`
as a wildcard to match a single character. `U*` equates to "begins with as a wildcard to match a single character. `U*` equates to "begins with
a 'U'"; `*U` equates to "ends with a 'U'"; and `*U*` equates to a 'U'"; `*U` equates to "ends with a 'U'"; and `*U*` equates to
"contains a 'U'". "contains a 'U'".
Note that PhpSpreadsheet recognizes wildcards only for equal/not-equal tests.
If you want to match explicitly against `*` or `?`, you can If you want to match explicitly against `*` or `?`, you can
escape it with a tilde `~`, so `?~**` would explicitly match for `*` escape it with a tilde `~`, so `?~**` would explicitly match for `*`
@ -290,8 +295,8 @@ This defined two rules, filtering numbers that are `>= -20` OR `<=
than OR. than OR.
```php ```php
$columnFilter->setAndOr( $columnFilter->setJoin(
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_COLUMN_ANDOR_AND \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND
); );
``` ```
@ -501,7 +506,7 @@ hiding all other rows within the autofilter area.
### Displaying Filtered Rows ### Displaying Filtered Rows
Simply looping through the rows in an autofilter area will still access Simply looping through the rows in an autofilter area will still access
ever row, whether it matches the filter criteria or not. To selectively every row, whether it matches the filter criteria or not. To selectively
access only the filtered rows, you need to test each rows visibility access only the filtered rows, you need to test each rows visibility
settings. settings.

View File

@ -6052,7 +6052,7 @@ parameters:
- -
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#" message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
count: 2 count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
- -
@ -6065,21 +6065,11 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
-
message: "#^Cannot access offset 'operator' on mixed\\.$#"
count: 2
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
- -
message: "#^Cannot access offset 'time' on mixed\\.$#" message: "#^Cannot access offset 'time' on mixed\\.$#"
count: 1 count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
-
message: "#^Cannot access offset 'value' on mixed\\.$#"
count: 9
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
- -
message: "#^Cannot use array destructuring on mixed\\.$#" message: "#^Cannot use array destructuring on mixed\\.$#"
count: 1 count: 1
@ -6120,11 +6110,6 @@ parameters:
count: 1 count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
-
message: "#^Parameter \\#2 \\$subject of function preg_match expects string, mixed given\\.$#"
count: 1
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
- -
message: "#^Parameter \\#3 \\$length of function array_slice expects int\\|null, mixed given\\.$#" message: "#^Parameter \\#3 \\$length of function array_slice expects int\\|null, mixed given\\.$#"
count: 1 count: 1

View File

@ -345,6 +345,7 @@ class AutoFilter
*/ */
private static function filterTestInCustomDataSet($cellValue, $ruleSet) private static function filterTestInCustomDataSet($cellValue, $ruleSet)
{ {
/** @var array[] */
$dataSet = $ruleSet['filterRules']; $dataSet = $ruleSet['filterRules'];
$join = $ruleSet['join']; $join = $ruleSet['join'];
$customRuleForBlanks = $ruleSet['customRuleForBlanks'] ?? false; $customRuleForBlanks = $ruleSet['customRuleForBlanks'] ?? false;
@ -357,38 +358,45 @@ class AutoFilter
} }
$returnVal = ($join == AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND); $returnVal = ($join == AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND);
foreach ($dataSet as $rule) { foreach ($dataSet as $rule) {
/** @var string */
$ruleValue = $rule['value'];
/** @var string */
$ruleOperator = $rule['operator'];
/** @var string */
$cellValueString = $cellValue;
$retVal = false; $retVal = false;
if (is_numeric($rule['value'])) { if (is_numeric($ruleValue)) {
// Numeric values are tested using the appropriate operator // Numeric values are tested using the appropriate operator
switch ($rule['operator']) { $numericTest = is_numeric($cellValue);
switch ($ruleOperator) {
case Rule::AUTOFILTER_COLUMN_RULE_EQUAL: case Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
$retVal = ($cellValue == $rule['value']); $retVal = $numericTest && ($cellValue == $ruleValue);
break; break;
case Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL: case Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
$retVal = ($cellValue != $rule['value']); $retVal = !$numericTest || ($cellValue != $ruleValue);
break; break;
case Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN: case Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN:
$retVal = ($cellValue > $rule['value']); $retVal = $numericTest && ($cellValue > $ruleValue);
break; break;
case Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL: case Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL:
$retVal = ($cellValue >= $rule['value']); $retVal = $numericTest && ($cellValue >= $ruleValue);
break; break;
case Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN: case Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN:
$retVal = ($cellValue < $rule['value']); $retVal = $numericTest && ($cellValue < $ruleValue);
break; break;
case Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL: case Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL:
$retVal = ($cellValue <= $rule['value']); $retVal = $numericTest && ($cellValue <= $ruleValue);
break; break;
} }
} elseif ($rule['value'] == '') { } elseif ($ruleValue == '') {
switch ($rule['operator']) { switch ($ruleOperator) {
case Rule::AUTOFILTER_COLUMN_RULE_EQUAL: case Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
$retVal = (($cellValue == '') || ($cellValue === null)); $retVal = (($cellValue == '') || ($cellValue === null));
@ -404,7 +412,32 @@ class AutoFilter
} }
} else { } else {
// String values are always tested for equality, factoring in for wildcards (hence a regexp test) // String values are always tested for equality, factoring in for wildcards (hence a regexp test)
$retVal = preg_match('/^' . $rule['value'] . '$/i', $cellValue); switch ($ruleOperator) {
case Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
$retVal = (bool) preg_match('/^' . $ruleValue . '$/i', $cellValueString);
break;
case Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
$retVal = !((bool) preg_match('/^' . $ruleValue . '$/i', $cellValueString));
break;
case Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN:
$retVal = strcasecmp($cellValueString, $ruleValue) > 0;
break;
case Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL:
$retVal = strcasecmp($cellValueString, $ruleValue) >= 0;
break;
case Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN:
$retVal = strcasecmp($cellValueString, $ruleValue) < 0;
break;
case Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL:
$retVal = strcasecmp($cellValueString, $ruleValue) <= 0;
break;
}
} }
// If there are multiple conditions, then we need to test both using the appropriate join operator // If there are multiple conditions, then we need to test both using the appropriate join operator
switch ($join) { switch ($join) {
@ -840,7 +873,7 @@ class AutoFilter
break; break;
case AutoFilter\Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER: case AutoFilter\Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER:
$customRuleForBlanks = false; $customRuleForBlanks = true;
$ruleValues = []; $ruleValues = [];
// Build a list of the filter value selections // Build a list of the filter value selections
foreach ($rules as $rule) { foreach ($rules as $rule) {

View File

@ -176,6 +176,9 @@ class Column
if (!in_array($filterType, self::$filterTypes)) { if (!in_array($filterType, self::$filterTypes)) {
throw new PhpSpreadsheetException('Invalid filter type for column AutoFilter.'); throw new PhpSpreadsheetException('Invalid filter type for column AutoFilter.');
} }
if ($filterType === self::AUTOFILTER_FILTERTYPE_CUSTOMFILTER && count($this->ruleset) > 2) {
throw new PhpSpreadsheetException('No more than 2 rules are allowed in a Custom Filter');
}
$this->filterType = $filterType; $this->filterType = $filterType;
@ -305,6 +308,9 @@ class Column
*/ */
public function createRule() public function createRule()
{ {
if ($this->filterType === self::AUTOFILTER_FILTERTYPE_CUSTOMFILTER && count($this->ruleset) >= 2) {
throw new PhpSpreadsheetException('No more than 2 rules are allowed in a Custom Filter');
}
$this->ruleset[] = new Column\Rule($this); $this->ruleset[] = new Column\Rule($this);
return end($this->ruleset); return end($this->ruleset);

View File

@ -125,15 +125,7 @@ class Rule
self::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE, self::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE,
]; ];
/* // Filter rule operators for filter and customFilter types.
* The only valid filter rule operators for filter and customFilter types are:
* <xsd:enumeration value="equal"/>
* <xsd:enumeration value="lessThan"/>
* <xsd:enumeration value="lessThanOrEqual"/>
* <xsd:enumeration value="notEqual"/>
* <xsd:enumeration value="greaterThanOrEqual"/>
* <xsd:enumeration value="greaterThan"/>
*/
const AUTOFILTER_COLUMN_RULE_EQUAL = 'equal'; const AUTOFILTER_COLUMN_RULE_EQUAL = 'equal';
const AUTOFILTER_COLUMN_RULE_NOTEQUAL = 'notEqual'; const AUTOFILTER_COLUMN_RULE_NOTEQUAL = 'notEqual';
const AUTOFILTER_COLUMN_RULE_GREATERTHAN = 'greaterThan'; const AUTOFILTER_COLUMN_RULE_GREATERTHAN = 'greaterThan';
@ -166,13 +158,9 @@ class Rule
self::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM, self::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM,
]; ];
// Rule Operators (Numeric, Boolean etc) // Unimplented Rule Operators (Numeric, Boolean etc)
// const AUTOFILTER_COLUMN_RULE_BETWEEN = 'between'; // greaterThanOrEqual 1 && lessThanOrEqual 2 // const AUTOFILTER_COLUMN_RULE_BETWEEN = 'between'; // greaterThanOrEqual 1 && lessThanOrEqual 2
// Rule Operators (Numeric Special) which are translated to standard numeric operators with calculated values // Rule Operators (Numeric Special) which are translated to standard numeric operators with calculated values
// const AUTOFILTER_COLUMN_RULE_TOPTEN = 'topTen'; // greaterThan calculated value
// const AUTOFILTER_COLUMN_RULE_TOPTENPERCENT = 'topTenPercent'; // greaterThan calculated value
// const AUTOFILTER_COLUMN_RULE_ABOVEAVERAGE = 'aboveAverage'; // Value is calculated as the average
// const AUTOFILTER_COLUMN_RULE_BELOWAVERAGE = 'belowAverage'; // Value is calculated as the average
// Rule Operators (String) which are set as wild-carded values // Rule Operators (String) which are set as wild-carded values
// const AUTOFILTER_COLUMN_RULE_BEGINSWITH = 'beginsWith'; // A* // const AUTOFILTER_COLUMN_RULE_BEGINSWITH = 'beginsWith'; // A*
// const AUTOFILTER_COLUMN_RULE_ENDSWITH = 'endsWith'; // *Z // const AUTOFILTER_COLUMN_RULE_ENDSWITH = 'endsWith'; // *Z
@ -181,24 +169,6 @@ class Rule
// Rule Operators (Date Special) which are translated to standard numeric operators with calculated values // Rule Operators (Date Special) which are translated to standard numeric operators with calculated values
// const AUTOFILTER_COLUMN_RULE_BEFORE = 'lessThan'; // const AUTOFILTER_COLUMN_RULE_BEFORE = 'lessThan';
// const AUTOFILTER_COLUMN_RULE_AFTER = 'greaterThan'; // const AUTOFILTER_COLUMN_RULE_AFTER = 'greaterThan';
// const AUTOFILTER_COLUMN_RULE_YESTERDAY = 'yesterday';
// const AUTOFILTER_COLUMN_RULE_TODAY = 'today';
// const AUTOFILTER_COLUMN_RULE_TOMORROW = 'tomorrow';
// const AUTOFILTER_COLUMN_RULE_LASTWEEK = 'lastWeek';
// const AUTOFILTER_COLUMN_RULE_THISWEEK = 'thisWeek';
// const AUTOFILTER_COLUMN_RULE_NEXTWEEK = 'nextWeek';
// const AUTOFILTER_COLUMN_RULE_LASTMONTH = 'lastMonth';
// const AUTOFILTER_COLUMN_RULE_THISMONTH = 'thisMonth';
// const AUTOFILTER_COLUMN_RULE_NEXTMONTH = 'nextMonth';
// const AUTOFILTER_COLUMN_RULE_LASTQUARTER = 'lastQuarter';
// const AUTOFILTER_COLUMN_RULE_THISQUARTER = 'thisQuarter';
// const AUTOFILTER_COLUMN_RULE_NEXTQUARTER = 'nextQuarter';
// const AUTOFILTER_COLUMN_RULE_LASTYEAR = 'lastYear';
// const AUTOFILTER_COLUMN_RULE_THISYEAR = 'thisYear';
// const AUTOFILTER_COLUMN_RULE_NEXTYEAR = 'nextYear';
// const AUTOFILTER_COLUMN_RULE_YEARTODATE = 'yearToDate'; // <dynamicFilter val="40909" type="yearToDate" maxVal="41113"/>
// const AUTOFILTER_COLUMN_RULE_ALLDATESINMONTH = 'allDatesInMonth'; // <dynamicFilter type="M2"/> for Month/February
// const AUTOFILTER_COLUMN_RULE_ALLDATESINQUARTER = 'allDatesInQuarter'; // <dynamicFilter type="Q2"/> for Quarter 2
/** /**
* Autofilter Column. * Autofilter Column.

View File

@ -0,0 +1,210 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class AutoFilterCustomNumericTest extends SetupTeardown
{
public function initsheet(): Worksheet
{
$sheet = $this->getSheet();
$sheet->getCell('A1')->setValue('Header');
$sheet->getCell('A2')->setValue(1);
$sheet->getCell('A3')->setValue(3);
$sheet->getCell('A4')->setValue(5);
// nothing in cell A5
$sheet->getCell('A6')->setValue(7);
$sheet->getCell('A7')->setValue(9);
$sheet->getCell('A8')->setValue(7);
$sheet->getCell('A9')->setValue(5);
$sheet->getCell('A10')->setValue(3);
$sheet->getCell('A11')->setValue(1);
$sheet->getCell('A12')->setValue('x');
$this->maxRow = 12;
return $sheet;
}
public function providerCustomRule(): array
{
return [
'equal to 3' => [[3, 10], Rule::AUTOFILTER_COLUMN_RULE_EQUAL, 3],
'not equal to 3' => [[2, 4, 5, 6, 7, 8, 9, 11, 12], Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL, 3],
'greater than 3' => [[4, 6, 7, 8, 9], Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN, 3],
'greater than or equal to 3' => [[3, 4, 6, 7, 8, 9, 10], Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL, 3],
'less than 3' => [[2, 11], Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN, 3],
'less than or equal to 3' => [[2, 3, 10, 11], Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL, 3],
];
}
/**
* @dataProvider providerCustomRule
*/
public function testCustomTest(array $expectedVisible, string $rule, int $comparand): void
{
$sheet = $this->initSheet();
$maxRow = $this->maxRow;
$autoFilter = $sheet->getAutoFilter();
$autoFilter->setRange("A1:A$maxRow");
$columnFilter = $autoFilter->getColumn('A');
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
$columnFilter->createRule()
->setRule(
$rule,
$comparand
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
self::assertEquals($expectedVisible, $this->getVisible());
}
public function testEqualsListSimple(): void
{
$sheet = $this->initSheet();
$maxRow = $this->maxRow;
$autoFilter = $sheet->getAutoFilter();
$autoFilter->setRange("A1:A$maxRow");
$columnFilter = $autoFilter->getColumn('A');
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
3
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
5
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
7
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
self::assertEquals([3, 4, 6, 8, 9, 10], $this->getVisible());
}
public function testEqualsList(): void
{
$sheet = $this->initSheet();
$maxRow = $this->maxRow;
$autoFilter = $sheet->getAutoFilter();
$autoFilter->setRange("A1:A$maxRow");
$columnFilter = $autoFilter->getColumn('A');
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
$columnFilter->setJoin(Column::AUTOFILTER_COLUMN_JOIN_OR);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
3
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
5
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
self::assertEquals([3, 4, 9, 10], $this->getVisible());
}
public function testNotEqualsList(): void
{
$sheet = $this->initSheet();
$maxRow = $this->maxRow;
$autoFilter = $sheet->getAutoFilter();
$autoFilter->setRange("A1:A$maxRow");
$columnFilter = $autoFilter->getColumn('A');
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
$columnFilter->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL,
3
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL,
5
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
self::assertEquals([2, 5, 6, 7, 8, 11, 12], $this->getVisible());
}
public function testNotEqualsListWith3Members(): void
{
$this->expectException(PhpSpreadsheetException::class);
$this->expectExceptionMessage('No more than 2 rules');
$sheet = $this->initSheet();
$maxRow = $this->maxRow;
$autoFilter = $sheet->getAutoFilter();
$autoFilter->setRange("A1:A$maxRow");
$columnFilter = $autoFilter->getColumn('A');
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
$columnFilter->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL,
3
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL,
5
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL,
7
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
self::assertEquals([2, 5, 7, 11, 12], $this->getVisible());
}
public function testNotEqualsListWith3MembersFilterTypeAfterRules(): void
{
$this->expectException(PhpSpreadsheetException::class);
$this->expectExceptionMessage('No more than 2 rules');
$sheet = $this->initSheet();
$maxRow = $this->maxRow;
$autoFilter = $sheet->getAutoFilter();
$autoFilter->setRange("A1:A$maxRow");
$columnFilter = $autoFilter->getColumn('A');
$columnFilter->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL,
3
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL,
5
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL,
7
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
self::assertEquals([2, 5, 7, 11, 12], $this->getVisible());
}
}

View File

@ -13,7 +13,7 @@ class AutoFilterCustomTextTest extends SetupTeardown
$sheet = $this->getSheet(); $sheet = $this->getSheet();
$sheet->getCell('A1')->setValue('Header'); $sheet->getCell('A1')->setValue('Header');
$sheet->getCell('A2')->setValue('abc'); $sheet->getCell('A2')->setValue('abc');
$sheet->getCell('A3')->setValue('cba'); $sheet->getCell('A3')->setValue('cBa');
$sheet->getCell('A4')->setValue('cab'); $sheet->getCell('A4')->setValue('cab');
// nothing in cell A5 // nothing in cell A5
$sheet->getCell('A6')->setValue('c*b'); $sheet->getCell('A6')->setValue('c*b');
@ -67,7 +67,7 @@ class AutoFilterCustomTextTest extends SetupTeardown
self::assertEquals($expectedVisible, $this->getVisible()); self::assertEquals($expectedVisible, $this->getVisible());
} }
public function testCustomTestNotEqual(): void public function testCustomTestNotEqualBlank(): void
{ {
$sheet = $this->initSheet(); $sheet = $this->initSheet();
$maxRow = $this->maxRow; $maxRow = $this->maxRow;
@ -85,7 +85,7 @@ class AutoFilterCustomTextTest extends SetupTeardown
self::assertEquals([2, 3, 4, 6, 7, 8, 9, 10, 11, 12], $this->getVisible()); self::assertEquals([2, 3, 4, 6, 7, 8, 9, 10, 11, 12], $this->getVisible());
} }
public function testCustomTestGreaterThan(): void public function testCustomTestNotEqualString(): void
{ {
$sheet = $this->initSheet(); $sheet = $this->initSheet();
$maxRow = $this->maxRow; $maxRow = $this->maxRow;
@ -95,11 +95,118 @@ class AutoFilterCustomTextTest extends SetupTeardown
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER); $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
$columnFilter->createRule() $columnFilter->createRule()
->setRule( ->setRule(
Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN, Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL,
'' 'cba'
) )
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER); ->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
self::assertEquals([2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], $this->getVisible()); self::assertEquals([2, 4, 5, 6, 7, 8, 9, 10, 11, 12], $this->getVisible());
}
public function testEqualsListSimple(): void
{
$sheet = $this->initSheet();
$maxRow = $this->maxRow;
$autoFilter = $sheet->getAutoFilter();
$autoFilter->setRange("A1:A$maxRow");
$columnFilter = $autoFilter->getColumn('A');
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_FILTER);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
'c?b' // simple filter - no wildcards
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
'a'
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_FILTER);
self::assertEquals([7, 8], $this->getVisible());
}
public function testEqualsList(): void
{
$sheet = $this->initSheet();
$maxRow = $this->maxRow;
$autoFilter = $sheet->getAutoFilter();
$autoFilter->setRange("A1:A$maxRow");
$columnFilter = $autoFilter->getColumn('A');
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
$columnFilter->setJoin(Column::AUTOFILTER_COLUMN_JOIN_OR);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
'a*'
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
'*c*'
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
self::assertEquals([2, 3, 4, 6, 7, 8, 9, 10], $this->getVisible());
}
public function testNotEqualsList(): void
{
$sheet = $this->initSheet();
$maxRow = $this->maxRow;
$autoFilter = $sheet->getAutoFilter();
$autoFilter->setRange("A1:A$maxRow");
$columnFilter = $autoFilter->getColumn('A');
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
$columnFilter->setJoin(Column::AUTOFILTER_COLUMN_JOIN_AND);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL,
'a*'
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
$columnFilter->createRule()
->setRule(
Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL,
'*c*'
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
self::assertEquals([5, 11, 12], $this->getVisible());
}
public function providerCustomRule(): array
{
return [
'equal to cba' => [[3], Rule::AUTOFILTER_COLUMN_RULE_EQUAL, 'cba'],
'not equal to cba' => [[2, 4, 5, 6, 7, 8, 9, 10, 11, 12], Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL, 'cba'],
'greater than cba' => [[9, 10, 11, 12], Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN, 'cba'],
'greater than or equal to cba' => [[3, 9, 10, 11, 12], Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL, 'cba'],
'less than cba' => [[2, 4, 5, 6, 7, 8], Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN, 'cba'],
'less than or equal to cba' => [[2, 3, 4, 5, 6, 7, 8], Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL, 'cba'],
];
}
/**
* @dataProvider providerCustomRule
*/
public function testCustomRuleTest(array $expectedVisible, string $rule, string $comparand): void
{
$sheet = $this->initSheet();
$maxRow = $this->maxRow;
$autoFilter = $sheet->getAutoFilter();
$autoFilter->setRange("A1:A$maxRow");
$columnFilter = $autoFilter->getColumn('A');
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER);
$columnFilter->createRule()
->setRule(
$rule,
$comparand
)
->setRuleType(Rule::AUTOFILTER_RULETYPE_CUSTOMFILTER);
self::assertEquals($expectedVisible, $this->getVisible());
} }
} }