diff --git a/CHANGELOG.md b/CHANGELOG.md index bdeaaa8ecd..6c64984403 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Added +- Support for date values and percentages in query parameters for Database functions, and the IF expressions in functions like COUNTIF() and AVERAGEIF(). [#1875](https://github.com/PHPOffice/PhpSpreadsheet/pull/1875) - Implemented DataBar for conditional formatting in Xlsx, providing read/write and creation of (type, value, direction, fills, border, axis position, color settings) as DataBar options in Excel. [#1754](https://github.com/PHPOffice/PhpSpreadsheet/pull/1754) - Alignment for ODS Writer [#1796](https://github.com/PHPOffice/PhpSpreadsheet/issues/1796) - Basic implementation of the PERMUTATIONA() Statistical Function diff --git a/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php b/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php index 22ce0957fb..ae2c3fd72f 100644 --- a/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php +++ b/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php @@ -83,8 +83,6 @@ protected static function getFilteredColumn(array $database, $field, array $crit } /** - * @TODO Support for Dates (including handling for >, <=, etc) - * @TODO Suport for formatted numerics (e.g. '>12.5%' => '>0.125') * @TODO Suport for wildcard ? and * in strings (includng escaping) */ private static function buildQuery(array $criteriaNames, array $criteria): string @@ -121,7 +119,9 @@ private static function executeQuery(array $database, string $query, $criteriaNa $testConditionList = $query; foreach ($criteriaNames as $key => $criteriaName) { $key = array_search($criteriaName, $fieldNames, true); - if (isset($dataValues[$key])) { + if (is_bool($dataValues[$key])) { + $dataValue = ($dataValues[$key]) ? 'TRUE' : 'FALSE'; + } elseif ($dataValues[$key] !== null) { $dataValue = $dataValues[$key]; $dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue; } else { @@ -129,7 +129,6 @@ private static function executeQuery(array $database, string $query, $criteriaNa } $testConditionList = str_replace('[:' . $criteriaName . ']', $dataValue, $testConditionList); } - // evaluate the criteria against the row data $result = Calculation::getInstance()->_calculateFormulaValue('=' . $testConditionList); // If the row failed to meet the criteria, remove it from the database diff --git a/src/PhpSpreadsheet/Calculation/Functions.php b/src/PhpSpreadsheet/Calculation/Functions.php index 2e8a7ecfcc..022e6be5e9 100644 --- a/src/PhpSpreadsheet/Calculation/Functions.php +++ b/src/PhpSpreadsheet/Calculation/Functions.php @@ -3,6 +3,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation; use PhpOffice\PhpSpreadsheet\Cell\Cell; +use PhpOffice\PhpSpreadsheet\Shared\Date; class Functions { @@ -252,9 +253,11 @@ public static function ifCondition($condition) if ($condition === '') { $condition = '=""'; } - if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='])) { - if (!is_numeric($condition)) { + $condition = self::operandSpecialHandling($condition); + if (is_bool($condition)) { + return '=' . ($condition ? 'TRUE' : 'FALSE'); + } elseif (!is_numeric($condition)) { $condition = Calculation::wrapResult(strtoupper($condition)); } @@ -263,9 +266,10 @@ public static function ifCondition($condition) preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches); [, $operator, $operand] = $matches; + $operand = self::operandSpecialHandling($operand); if (is_numeric(trim($operand, '"'))) { $operand = trim($operand, '"'); - } elseif (!is_numeric($operand)) { + } elseif (!is_numeric($operand) && $operand !== 'FALSE' && $operand !== 'TRUE') { $operand = str_replace('"', '""', $operand); $operand = Calculation::wrapResult(strtoupper($operand)); } @@ -273,6 +277,27 @@ public static function ifCondition($condition) return str_replace('""""', '""', $operator . $operand); } + private static function operandSpecialHandling($operand) + { + if (is_numeric($operand) || is_bool($operand)) { + return $operand; + } elseif (strtoupper($operand) === Calculation::getTRUE() || strtoupper($operand) === Calculation::getFALSE()) { + return strtoupper($operand); + } + + // Check for percentage + if (preg_match('/^\-?\d*\.?\d*\s?\%$/', $operand)) { + return ((float) rtrim($operand, '%')) / 100; + } + + // Check for dates + if (($dateValueOperand = Date::stringToExcel($operand)) !== false) { + return $dateValueOperand; + } + + return $operand; + } + /** * ERROR_TYPE. * diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DCountATest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DCountATest.php index ea856c57de..4cfea42f75 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DCountATest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DCountATest.php @@ -100,7 +100,7 @@ public function providerDCountA() 'Score', [ ['Subject', 'Score'], - ['English', '>0.60'], + ['English', '>60%'], ], ], ]; diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DCountTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DCountTest.php index 9e12ac96ba..11d8adabfd 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DCountTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DCountTest.php @@ -59,6 +59,21 @@ protected function database2() ]; } + protected function database3() + { + return [ + ['Status', 'Value'], + [false, 1], + [true, 2], + [true, 4], + [false, 8], + [true, 16], + [false, 32], + [false, 64], + [false, 128], + ]; + } + public function providerDCount() { return [ @@ -95,7 +110,25 @@ public function providerDCount() null, [ ['Subject', 'Score'], - ['English', '>0.63'], + ['English', '>63%'], + ], + ], + [ + 3, + $this->database3(), + 'Value', + [ + ['Status'], + [true], + ], + ], + [ + 5, + $this->database3(), + 'Value', + [ + ['Status'], + ['<>true'], ], ], ]; diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DProductTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DProductTest.php index 9b2f92b3d2..0fb1bba739 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DProductTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DProductTest.php @@ -73,8 +73,6 @@ public function providerDProduct() ['=Pear', null, null], ], ], - /* - * We don't yet support date handling in the search query [ 36, $this->database2(), @@ -93,7 +91,6 @@ public function providerDProduct() ['Test1', '<05-Jan-2017'], ], ], - */ [ null, $this->database1(), diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DSumTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DSumTest.php index fbb86bb92f..2a197cd114 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DSumTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Database/DSumTest.php @@ -95,7 +95,7 @@ public function providerDSum() ], ], /* - * We don't yet support woldcards in text search fields + * We don't yet support wildcards in text search fields [ 710000, $this->database2(),