diff --git a/docs/topics/autofilters.md b/docs/topics/autofilters.md index cd4291f4..d8f37b60 100644 --- a/docs/topics/autofilters.md +++ b/docs/topics/autofilters.md @@ -99,6 +99,8 @@ results are unpredictable. Other filter expression types (such as cell colour filters) are not yet supported. +String comparisons in filters are case-insensitive. + ### Simple filters 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 filter type. In this case, we're just going to specify that this 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 $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 a 'U'"; `*U` equates to "ends with a 'U'"; and `*U*` equates to "contains a 'U'". +Note that PhpSpreadsheet recognizes wildcards only for equal/not-equal tests. If you want to match explicitly against `*` or `?`, you can 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. ```php -$columnFilter->setAndOr( - \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_COLUMN_ANDOR_AND +$columnFilter->setJoin( + \PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND ); ``` @@ -501,7 +506,7 @@ hiding all other rows within the autofilter area. ### Displaying Filtered Rows 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 row’s visibility settings. diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 4d4b6e1b..bcdba204 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -6052,7 +6052,7 @@ parameters: - message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#" - count: 2 + count: 1 path: src/PhpSpreadsheet/Worksheet/AutoFilter.php - @@ -6065,21 +6065,11 @@ parameters: count: 1 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\\.$#" count: 1 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\\.$#" count: 1 @@ -6120,11 +6110,6 @@ parameters: count: 1 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\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Worksheet/AutoFilter.php b/src/PhpSpreadsheet/Worksheet/AutoFilter.php index 4f857a82..4fad6062 100644 --- a/src/PhpSpreadsheet/Worksheet/AutoFilter.php +++ b/src/PhpSpreadsheet/Worksheet/AutoFilter.php @@ -345,6 +345,7 @@ class AutoFilter */ private static function filterTestInCustomDataSet($cellValue, $ruleSet) { + /** @var array[] */ $dataSet = $ruleSet['filterRules']; $join = $ruleSet['join']; $customRuleForBlanks = $ruleSet['customRuleForBlanks'] ?? false; @@ -357,38 +358,45 @@ class AutoFilter } $returnVal = ($join == AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND); foreach ($dataSet as $rule) { + /** @var string */ + $ruleValue = $rule['value']; + /** @var string */ + $ruleOperator = $rule['operator']; + /** @var string */ + $cellValueString = $cellValue; $retVal = false; - if (is_numeric($rule['value'])) { + if (is_numeric($ruleValue)) { // Numeric values are tested using the appropriate operator - switch ($rule['operator']) { + $numericTest = is_numeric($cellValue); + switch ($ruleOperator) { case Rule::AUTOFILTER_COLUMN_RULE_EQUAL: - $retVal = ($cellValue == $rule['value']); + $retVal = $numericTest && ($cellValue == $ruleValue); break; case Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL: - $retVal = ($cellValue != $rule['value']); + $retVal = !$numericTest || ($cellValue != $ruleValue); break; case Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN: - $retVal = ($cellValue > $rule['value']); + $retVal = $numericTest && ($cellValue > $ruleValue); break; case Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL: - $retVal = ($cellValue >= $rule['value']); + $retVal = $numericTest && ($cellValue >= $ruleValue); break; case Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN: - $retVal = ($cellValue < $rule['value']); + $retVal = $numericTest && ($cellValue < $ruleValue); break; case Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL: - $retVal = ($cellValue <= $rule['value']); + $retVal = $numericTest && ($cellValue <= $ruleValue); break; } - } elseif ($rule['value'] == '') { - switch ($rule['operator']) { + } elseif ($ruleValue == '') { + switch ($ruleOperator) { case Rule::AUTOFILTER_COLUMN_RULE_EQUAL: $retVal = (($cellValue == '') || ($cellValue === null)); @@ -404,7 +412,32 @@ class AutoFilter } } else { // 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 switch ($join) { @@ -840,7 +873,7 @@ class AutoFilter break; case AutoFilter\Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER: - $customRuleForBlanks = false; + $customRuleForBlanks = true; $ruleValues = []; // Build a list of the filter value selections foreach ($rules as $rule) { diff --git a/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php b/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php index 48aead7d..ef5396f3 100644 --- a/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php +++ b/src/PhpSpreadsheet/Worksheet/AutoFilter/Column.php @@ -176,6 +176,9 @@ class Column if (!in_array($filterType, self::$filterTypes)) { 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; @@ -305,6 +308,9 @@ class Column */ 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); return end($this->ruleset); diff --git a/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php b/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php index 742a7b8a..525b06f6 100644 --- a/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php +++ b/src/PhpSpreadsheet/Worksheet/AutoFilter/Column/Rule.php @@ -125,15 +125,7 @@ class Rule self::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE, ]; - /* - * The only valid filter rule operators for filter and customFilter types are: - * - * - * - * - * - * - */ + // Filter rule operators for filter and customFilter types. const AUTOFILTER_COLUMN_RULE_EQUAL = 'equal'; const AUTOFILTER_COLUMN_RULE_NOTEQUAL = 'notEqual'; const AUTOFILTER_COLUMN_RULE_GREATERTHAN = 'greaterThan'; @@ -166,39 +158,17 @@ class Rule self::AUTOFILTER_COLUMN_RULE_TOPTEN_BOTTOM, ]; - // Rule Operators (Numeric, Boolean etc) -// const AUTOFILTER_COLUMN_RULE_BETWEEN = 'between'; // greaterThanOrEqual 1 && lessThanOrEqual 2 + // Unimplented Rule Operators (Numeric, Boolean etc) + // const AUTOFILTER_COLUMN_RULE_BETWEEN = 'between'; // greaterThanOrEqual 1 && lessThanOrEqual 2 // 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 -// const AUTOFILTER_COLUMN_RULE_BEGINSWITH = 'beginsWith'; // A* -// const AUTOFILTER_COLUMN_RULE_ENDSWITH = 'endsWith'; // *Z -// const AUTOFILTER_COLUMN_RULE_CONTAINS = 'contains'; // *B* -// const AUTOFILTER_COLUMN_RULE_DOESNTCONTAIN = 'notEqual'; // notEqual *B* + // const AUTOFILTER_COLUMN_RULE_BEGINSWITH = 'beginsWith'; // A* + // const AUTOFILTER_COLUMN_RULE_ENDSWITH = 'endsWith'; // *Z + // const AUTOFILTER_COLUMN_RULE_CONTAINS = 'contains'; // *B* + // const AUTOFILTER_COLUMN_RULE_DOESNTCONTAIN = 'notEqual'; // notEqual *B* // Rule Operators (Date Special) which are translated to standard numeric operators with calculated values -// const AUTOFILTER_COLUMN_RULE_BEFORE = 'lessThan'; -// 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'; // -// const AUTOFILTER_COLUMN_RULE_ALLDATESINMONTH = 'allDatesInMonth'; // for Month/February -// const AUTOFILTER_COLUMN_RULE_ALLDATESINQUARTER = 'allDatesInQuarter'; // for Quarter 2 + // const AUTOFILTER_COLUMN_RULE_BEFORE = 'lessThan'; + // const AUTOFILTER_COLUMN_RULE_AFTER = 'greaterThan'; /** * Autofilter Column. diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterCustomNumericTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterCustomNumericTest.php new file mode 100644 index 00000000..792e92e9 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterCustomNumericTest.php @@ -0,0 +1,210 @@ +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()); + } +} diff --git a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterCustomTextTest.php b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterCustomTextTest.php index 643ffc0f..da2682d6 100644 --- a/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterCustomTextTest.php +++ b/tests/PhpSpreadsheetTests/Worksheet/AutoFilter/AutoFilterCustomTextTest.php @@ -13,7 +13,7 @@ class AutoFilterCustomTextTest extends SetupTeardown $sheet = $this->getSheet(); $sheet->getCell('A1')->setValue('Header'); $sheet->getCell('A2')->setValue('abc'); - $sheet->getCell('A3')->setValue('cba'); + $sheet->getCell('A3')->setValue('cBa'); $sheet->getCell('A4')->setValue('cab'); // nothing in cell A5 $sheet->getCell('A6')->setValue('c*b'); @@ -67,7 +67,7 @@ class AutoFilterCustomTextTest extends SetupTeardown self::assertEquals($expectedVisible, $this->getVisible()); } - public function testCustomTestNotEqual(): void + public function testCustomTestNotEqualBlank(): void { $sheet = $this->initSheet(); $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()); } - public function testCustomTestGreaterThan(): void + public function testCustomTestNotEqualString(): void { $sheet = $this->initSheet(); $maxRow = $this->maxRow; @@ -95,11 +95,118 @@ class AutoFilterCustomTextTest extends SetupTeardown $columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_CUSTOMFILTER); $columnFilter->createRule() ->setRule( - Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN, - '' + Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL, + 'cba' ) ->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()); } }