diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c64984403..f8baacc01e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,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) +- Support for booleans, and for wildcard text search in query parameters for Database functions. [#1876](https://github.com/PHPOffice/PhpSpreadsheet/pull/1876) - 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/Calculation.php b/src/PhpSpreadsheet/Calculation/Calculation.php index 87bf44a516..ba6299572f 100644 --- a/src/PhpSpreadsheet/Calculation/Calculation.php +++ b/src/PhpSpreadsheet/Calculation/Calculation.php @@ -2663,12 +2663,16 @@ class Calculation private static $controlFunctions = [ 'MKMATRIX' => [ 'argumentCount' => '*', - 'functionCall' => [__CLASS__, 'mkMatrix'], + 'functionCall' => [Internal\MakeMatrix::class, 'make'], ], 'NAME.ERROR' => [ 'argumentCount' => '*', 'functionCall' => [Functions::class, 'NAME'], ], + 'WILDCARDMATCH' => [ + 'argumentCount' => '2', + 'functionCall' => [Internal\WildcardMatch::class, 'compare'], + ], ]; public function __construct(?Spreadsheet $spreadsheet = null) @@ -3742,11 +3746,6 @@ private function convertMatrixReferences($formula) return $formula; } - private static function mkMatrix(...$args) - { - return $args; - } - // Binary Operators // These operators always work on two values // Array key is the operator, the value indicates whether this is a left or right associative operator diff --git a/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php b/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php index ae2c3fd72f..a08f1251fc 100644 --- a/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php +++ b/src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php @@ -4,6 +4,7 @@ use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Functions; +use PhpOffice\PhpSpreadsheet\Calculation\Internal\WildcardMatch; abstract class DatabaseAbstract { @@ -82,9 +83,6 @@ protected static function getFilteredColumn(array $database, $field, array $crit return $columnData; } - /** - * @TODO Suport for wildcard ? and * in strings (includng escaping) - */ private static function buildQuery(array $criteriaNames, array $criteria): string { $baseQuery = []; @@ -92,7 +90,7 @@ private static function buildQuery(array $criteriaNames, array $criteria): strin foreach ($criterion as $field => $value) { $criterionName = $criteriaNames[$field]; if ($value !== null && $value !== '') { - $condition = '[:' . $criterionName . ']' . Functions::ifCondition($value); + $condition = self::buildCondition($value, $criterionName); $baseQuery[$key][] = $condition; } } @@ -108,31 +106,39 @@ function ($rowValue) { return (count($rowQuery) > 1) ? 'OR(' . implode(',', $rowQuery) . ')' : $rowQuery[0]; } - /** - * @param $criteriaNames - * @param $fieldNames - */ - private static function executeQuery(array $database, string $query, $criteriaNames, $fieldNames): array + private static function buildCondition($criterion, string $criterionName): string + { + $ifCondition = Functions::ifCondition($criterion); + + // Check for wildcard characters used in the condition + $result = preg_match('/(?[^"]*)(?".*[*?].*")/ui', $ifCondition, $matches); + if ($result !== 1) { + return "[:{$criterionName}]{$ifCondition}"; + } + + $trueFalse = ($matches['operator'] !== '<>'); + $wildcard = WildcardMatch::wildcard($matches['operand']); + $condition = "WILDCARDMATCH([:{$criterionName}],{$wildcard})"; + if ($trueFalse === false) { + $condition = "NOT({$condition})"; + } + + return $condition; + } + + private static function executeQuery(array $database, string $query, array $criteria, array $fields): array { foreach ($database as $dataRow => $dataValues) { // Substitute actual values from the database row for our [:placeholders] - $testConditionList = $query; - foreach ($criteriaNames as $key => $criteriaName) { - $key = array_search($criteriaName, $fieldNames, true); - 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 { - $dataValue = 'NULL'; - } - $testConditionList = str_replace('[:' . $criteriaName . ']', $dataValue, $testConditionList); + $conditions = $query; + foreach ($criteria as $criterion) { + $conditions = self::processCondition($criterion, $fields, $dataValues, $conditions); } + // 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 + $result = Calculation::getInstance()->_calculateFormulaValue('=' . $conditions); + // If the row failed to meet the criteria, remove it from the database if ($result !== true) { unset($database[$dataRow]); } @@ -140,4 +146,19 @@ private static function executeQuery(array $database, string $query, $criteriaNa return $database; } + + private static function processCondition(string $criterion, array $fields, array $dataValues, string $conditions) + { + $key = array_search($criterion, $fields, true); + + $dataValue = 'NULL'; + 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; + } + + return str_replace('[:' . $criterion . ']', $dataValue, $conditions); + } } diff --git a/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php b/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php new file mode 100644 index 0000000000..8b53464fc4 --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/Internal/MakeMatrix.php @@ -0,0 +1,11 @@ +2', 'North'], ], ], - /* - * We don't yet support wildcards in text search fields [ 710000, $this->database2(), @@ -105,7 +103,6 @@ public function providerDSum() ['3', 'C*'], ], ], - */ [ null, $this->database1(),