Skip to content

Commit 40a6dee

Browse files
author
Mark Baker
authored
Enable support for dates and percentages in Excel Database functions (#1875)
* Enable support for dates and percentages in Excel Database functions, and CountIf/AverageIf/etc * Enable support for booleans in Excel Database functions
1 parent 3764f30 commit 40a6dee

File tree

7 files changed

+68
-13
lines changed

7 files changed

+68
-13
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
99

1010
### Added
1111

12+
- 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)
1213
- 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)
1314
- Alignment for ODS Writer [#1796](https://github.com/PHPOffice/PhpSpreadsheet/issues/1796)
1415
- Basic implementation of the PERMUTATIONA() Statistical Function

src/PhpSpreadsheet/Calculation/Database/DatabaseAbstract.php

+3-4
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,6 @@ protected static function getFilteredColumn(array $database, $field, array $crit
8383
}
8484

8585
/**
86-
* @TODO Support for Dates (including handling for >, <=, etc)
87-
* @TODO Suport for formatted numerics (e.g. '>12.5%' => '>0.125')
8886
* @TODO Suport for wildcard ? and * in strings (includng escaping)
8987
*/
9088
private static function buildQuery(array $criteriaNames, array $criteria): string
@@ -121,15 +119,16 @@ private static function executeQuery(array $database, string $query, $criteriaNa
121119
$testConditionList = $query;
122120
foreach ($criteriaNames as $key => $criteriaName) {
123121
$key = array_search($criteriaName, $fieldNames, true);
124-
if (isset($dataValues[$key])) {
122+
if (is_bool($dataValues[$key])) {
123+
$dataValue = ($dataValues[$key]) ? 'TRUE' : 'FALSE';
124+
} elseif ($dataValues[$key] !== null) {
125125
$dataValue = $dataValues[$key];
126126
$dataValue = (is_string($dataValue)) ? Calculation::wrapResult(strtoupper($dataValue)) : $dataValue;
127127
} else {
128128
$dataValue = 'NULL';
129129
}
130130
$testConditionList = str_replace('[:' . $criteriaName . ']', $dataValue, $testConditionList);
131131
}
132-
133132
// evaluate the criteria against the row data
134133
$result = Calculation::getInstance()->_calculateFormulaValue('=' . $testConditionList);
135134
// If the row failed to meet the criteria, remove it from the database

src/PhpSpreadsheet/Calculation/Functions.php

+28-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PhpOffice\PhpSpreadsheet\Calculation;
44

55
use PhpOffice\PhpSpreadsheet\Cell\Cell;
6+
use PhpOffice\PhpSpreadsheet\Shared\Date;
67

78
class Functions
89
{
@@ -252,9 +253,11 @@ public static function ifCondition($condition)
252253
if ($condition === '') {
253254
$condition = '=""';
254255
}
255-
256256
if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='])) {
257-
if (!is_numeric($condition)) {
257+
$condition = self::operandSpecialHandling($condition);
258+
if (is_bool($condition)) {
259+
return '=' . ($condition ? 'TRUE' : 'FALSE');
260+
} elseif (!is_numeric($condition)) {
258261
$condition = Calculation::wrapResult(strtoupper($condition));
259262
}
260263

@@ -263,16 +266,38 @@ public static function ifCondition($condition)
263266
preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches);
264267
[, $operator, $operand] = $matches;
265268

269+
$operand = self::operandSpecialHandling($operand);
266270
if (is_numeric(trim($operand, '"'))) {
267271
$operand = trim($operand, '"');
268-
} elseif (!is_numeric($operand)) {
272+
} elseif (!is_numeric($operand) && $operand !== 'FALSE' && $operand !== 'TRUE') {
269273
$operand = str_replace('"', '""', $operand);
270274
$operand = Calculation::wrapResult(strtoupper($operand));
271275
}
272276

273277
return str_replace('""""', '""', $operator . $operand);
274278
}
275279

280+
private static function operandSpecialHandling($operand)
281+
{
282+
if (is_numeric($operand) || is_bool($operand)) {
283+
return $operand;
284+
} elseif (strtoupper($operand) === Calculation::getTRUE() || strtoupper($operand) === Calculation::getFALSE()) {
285+
return strtoupper($operand);
286+
}
287+
288+
// Check for percentage
289+
if (preg_match('/^\-?\d*\.?\d*\s?\%$/', $operand)) {
290+
return ((float) rtrim($operand, '%')) / 100;
291+
}
292+
293+
// Check for dates
294+
if (($dateValueOperand = Date::stringToExcel($operand)) !== false) {
295+
return $dateValueOperand;
296+
}
297+
298+
return $operand;
299+
}
300+
276301
/**
277302
* ERROR_TYPE.
278303
*

tests/PhpSpreadsheetTests/Calculation/Functions/Database/DCountATest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public function providerDCountA()
100100
'Score',
101101
[
102102
['Subject', 'Score'],
103-
['English', '>0.60'],
103+
['English', '>60%'],
104104
],
105105
],
106106
];

tests/PhpSpreadsheetTests/Calculation/Functions/Database/DCountTest.php

+34-1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,21 @@ protected function database2()
5959
];
6060
}
6161

62+
protected function database3()
63+
{
64+
return [
65+
['Status', 'Value'],
66+
[false, 1],
67+
[true, 2],
68+
[true, 4],
69+
[false, 8],
70+
[true, 16],
71+
[false, 32],
72+
[false, 64],
73+
[false, 128],
74+
];
75+
}
76+
6277
public function providerDCount()
6378
{
6479
return [
@@ -95,7 +110,25 @@ public function providerDCount()
95110
null,
96111
[
97112
['Subject', 'Score'],
98-
['English', '>0.63'],
113+
['English', '>63%'],
114+
],
115+
],
116+
[
117+
3,
118+
$this->database3(),
119+
'Value',
120+
[
121+
['Status'],
122+
[true],
123+
],
124+
],
125+
[
126+
5,
127+
$this->database3(),
128+
'Value',
129+
[
130+
['Status'],
131+
['<>true'],
99132
],
100133
],
101134
];

tests/PhpSpreadsheetTests/Calculation/Functions/Database/DProductTest.php

-3
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,6 @@ public function providerDProduct()
7373
['=Pear', null, null],
7474
],
7575
],
76-
/*
77-
* We don't yet support date handling in the search query
7876
[
7977
36,
8078
$this->database2(),
@@ -93,7 +91,6 @@ public function providerDProduct()
9391
['Test1', '<05-Jan-2017'],
9492
],
9593
],
96-
*/
9794
[
9895
null,
9996
$this->database1(),

tests/PhpSpreadsheetTests/Calculation/Functions/Database/DSumTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public function providerDSum()
9595
],
9696
],
9797
/*
98-
* We don't yet support woldcards in text search fields
98+
* We don't yet support wildcards in text search fields
9999
[
100100
710000,
101101
$this->database2(),

0 commit comments

Comments
 (0)