Autofilter Part 1
Most of the remaining 32-bit-unsafe date handling that remains in PhpSpreadsheet is in AutoFilter. Cleaning this up demonstrated that there are a lot of problems with AutoFilter, and I will do it in (probably two) pieces. In this PR: - Dynamic date processing was really wrong. There were no tests nor samples to exercise this code. (If you need details, you can try running the new sample against old code.) It is completely re-written. - ThisYear/Month/Week/Quarter had been omitted. - Rules such as AUTOFILTER_RULETYPE_DYNAMIC_MONTH_2 were almost correct, but showed some off-by-1 errors. I suspect these were timezone-related, and therefore more obvious to those of us far away from Greenwich. - All Autofilter tests are moved to a single directory. - The documentation suggested using null with the Dynamic Date setup, but Phpstan did not like that in my new tests/samples. Rather than change the doc block, I changed the documentation to suggest null string. - I created a new sample to generate sheets using all the dynamic filters. - I have added some new unit tests for each of the dynamic filters. I would love to be able to add some "time travel" tests because the dynamic nature of the filter makes most of the results change from day to day, which presents significant challenges in writing comprehensive unit tests (the same is true for code coverage). I was not able to find a good way to simulate time within PhpUnit, but the Linux 'faketime' package was extraordinarily easy and helpful in allowing me to confirm some edge cases. I had less satisfactory results with some Windows equivalents, but was still able to run some tests. - Code coverage increases from below 60% to above 80%. To be done: - Some 32-bit unsafe dates remain in filterTestInDateGroupSet. - Also in some of the existing AutoFilter samples. - Study existing unit tests for AutoFilter which use mocking to see if they can/should be replaced with 'real' tests. - Improve code coverage in AutoFilter, AutoFilter/Column, and AutoFilter/Common/Rule.
This commit is contained in:
parent
bff2317a03
commit
b19fcef51f
|
|
@ -327,14 +327,14 @@ $columnFilter->setFilterType(
|
||||||
```
|
```
|
||||||
|
|
||||||
When defining the rule for a dynamic filter, we don't define a value (we
|
When defining the rule for a dynamic filter, we don't define a value (we
|
||||||
can simply set that to NULL) but we do specify the dynamic filter
|
can simply set that to null string) but we do specify the dynamic filter
|
||||||
category.
|
category.
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$columnFilter->createRule()
|
$columnFilter->createRule()
|
||||||
->setRule(
|
->setRule(
|
||||||
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
|
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
|
||||||
NULL,
|
'',
|
||||||
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE
|
\PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE
|
||||||
)
|
)
|
||||||
->setRuleType(
|
->setRuleType(
|
||||||
|
|
|
||||||
|
|
@ -5417,7 +5417,7 @@ parameters:
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#1 \\$excelTimestamp of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Date\\:\\:excelToTimestamp\\(\\) expects float\\|int, float\\|int\\|string given\\.$#"
|
message: "#^Parameter \\#1 \\$excelTimestamp of static method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Date\\:\\:excelToTimestamp\\(\\) expects float\\|int, float\\|int\\|string given\\.$#"
|
||||||
count: 2
|
count: 1
|
||||||
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
|
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
|
||||||
|
|
||||||
-
|
-
|
||||||
|
|
@ -5430,36 +5430,6 @@ parameters:
|
||||||
count: 1
|
count: 1
|
||||||
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
|
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#2 \\$now of function strtotime expects int, int\\|false given\\.$#"
|
|
||||||
count: 6
|
|
||||||
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#5 \\$day of function gmmktime expects int, string given\\.$#"
|
|
||||||
count: 8
|
|
||||||
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#6 \\$year of function gmmktime expects int, string given\\.$#"
|
|
||||||
count: 13
|
|
||||||
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#4 \\$mon of function gmmktime expects int, string given\\.$#"
|
|
||||||
count: 2
|
|
||||||
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#5 \\$day of function gmmktime expects int, float given\\.$#"
|
|
||||||
count: 2
|
|
||||||
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\$attributes of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\:\\:setAttributes\\(\\) expects array\\<string\\>, array\\<string, float\\|int\\|null\\> given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: src/PhpSpreadsheet/Worksheet/AutoFilter.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has no return typehint specified\\.$#"
|
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:calculateTopTenValue\\(\\) has no return typehint specified\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
|
@ -7445,21 +7415,11 @@ parameters:
|
||||||
count: 1
|
count: 1
|
||||||
path: tests/PhpSpreadsheetTests/Worksheet/AutoFilter/Column/RuleTest.php
|
path: tests/PhpSpreadsheetTests/Worksheet/AutoFilter/Column/RuleTest.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\$attributes of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\:\\:setAttributes\\(\\) expects array\\<string\\>, array\\<string, int\\> given\\.$#"
|
|
||||||
count: 3
|
|
||||||
path: tests/PhpSpreadsheetTests/Worksheet/AutoFilter/ColumnTest.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#2 \\$pValue of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\:\\:setAttribute\\(\\) expects string, int given\\.$#"
|
message: "#^Parameter \\#2 \\$pValue of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\:\\:setAttribute\\(\\) expects string, int given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: tests/PhpSpreadsheetTests/Worksheet/AutoFilter/ColumnTest.php
|
path: tests/PhpSpreadsheetTests/Worksheet/AutoFilter/ColumnTest.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Parameter \\#1 \\$pColumn of method PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\:\\:setColumn\\(\\) expects PhpOffice\\\\PhpSpreadsheet\\\\Worksheet\\\\AutoFilter\\\\Column\\|string, float given\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: tests/PhpSpreadsheetTests/Worksheet/AutoFilterTest.php
|
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Parameter \\#1 \\$im of function imagecolorallocate expects resource, resource\\|false given\\.$#"
|
message: "#^Parameter \\#1 \\$im of function imagecolorallocate expects resource, resource\\|false given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
|
||||||
|
|
||||||
|
require __DIR__ . '/../Header.php';
|
||||||
|
|
||||||
|
// Sample can be slightly off if processing begins just before midnight
|
||||||
|
// and does not complete till after midnight.
|
||||||
|
// This possibility is accounted for in unit tests,
|
||||||
|
// but seems unneccesarily complicated for the sample.
|
||||||
|
|
||||||
|
function createSheet(Spreadsheet $spreadsheet, string $rule): void
|
||||||
|
{
|
||||||
|
$sheet = $spreadsheet->createSheet();
|
||||||
|
$sheet->setTitle($rule);
|
||||||
|
$sheet->getCell('A1')->setValue('Date');
|
||||||
|
$row = 1;
|
||||||
|
$date = new DateTime();
|
||||||
|
$year = (int) $date->format('Y');
|
||||||
|
$month = (int) $date->format('m');
|
||||||
|
$day = (int) $date->format('d');
|
||||||
|
$yearMinus2 = $year - 2;
|
||||||
|
$sheet->getCell('B1')->setValue("=DATE($year, $month, $day)");
|
||||||
|
// Each day for two weeks before today through 2 weeks after
|
||||||
|
for ($dayOffset = -14; $dayOffset < 14; ++$dayOffset) {
|
||||||
|
++$row;
|
||||||
|
$sheet->getCell("A$row")->setValue("=B1+($dayOffset)");
|
||||||
|
}
|
||||||
|
// First and last day of each month, starting with January 2 years before,
|
||||||
|
// through December 2 years after.
|
||||||
|
for ($monthOffset = 0; $monthOffset < 48; ++$monthOffset) {
|
||||||
|
++$row;
|
||||||
|
$sheet->getCell("A$row")->setValue("=DATE($yearMinus2, $monthOffset, 1)");
|
||||||
|
++$row;
|
||||||
|
$sheet->getCell("A$row")->setValue("=DATE($yearMinus2, $monthOffset + 1, 0)");
|
||||||
|
}
|
||||||
|
$sheet->getStyle("A2:A$row")->getNumberFormat()->setFormatCode('yyyy-mm-dd');
|
||||||
|
$sheet->getStyle('B1')->getNumberFormat()->setFormatCode('yyyy-mm-dd');
|
||||||
|
$sheet->getColumnDimension('A')->setAutoSize(true);
|
||||||
|
$sheet->getColumnDimension('B')->setAutoSize(true);
|
||||||
|
$autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter();
|
||||||
|
$autoFilter->setRange("A1:A$row");
|
||||||
|
$columnFilter = $autoFilter->getColumn('A');
|
||||||
|
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
|
||||||
|
$columnFilter->createRule()
|
||||||
|
->setRule(
|
||||||
|
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
|
||||||
|
'',
|
||||||
|
$rule
|
||||||
|
)
|
||||||
|
->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
|
||||||
|
$sheet->setSelectedCell('B1');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new Spreadsheet object
|
||||||
|
$helper->log('Create new Spreadsheet object');
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
|
||||||
|
// Set document properties
|
||||||
|
$helper->log('Set document properties');
|
||||||
|
$spreadsheet->getProperties()->setCreator('Owen Leibman')
|
||||||
|
->setLastModifiedBy('Owen Leibman')
|
||||||
|
->setTitle('PhpSpreadsheet Test Document')
|
||||||
|
->setSubject('PhpSpreadsheet Test Document')
|
||||||
|
->setDescription('Test document for PhpSpreadsheet, generated using PHP classes.')
|
||||||
|
->setKeywords('office PhpSpreadsheet php')
|
||||||
|
->setCategory('Test result file');
|
||||||
|
|
||||||
|
$ruleNames = [
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_TODAY,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_2,
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_3,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Create the worksheets
|
||||||
|
foreach ($ruleNames as $ruleName) {
|
||||||
|
$helper->log("Add data and filter for $ruleName");
|
||||||
|
createSheet($spreadsheet, $ruleName);
|
||||||
|
}
|
||||||
|
$spreadsheet->removeSheetByIndex(0);
|
||||||
|
$spreadsheet->setActiveSheetIndex(0);
|
||||||
|
// Save
|
||||||
|
$helper->write($spreadsheet, __FILE__);
|
||||||
|
|
@ -2,12 +2,14 @@
|
||||||
|
|
||||||
namespace PhpOffice\PhpSpreadsheet\Worksheet;
|
namespace PhpOffice\PhpSpreadsheet\Worksheet;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use DateTimeZone;
|
||||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||||
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
|
|
||||||
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
|
||||||
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
|
||||||
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
|
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
|
||||||
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
use PhpOffice\PhpSpreadsheet\Shared\Date;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
|
||||||
|
|
||||||
class AutoFilter
|
class AutoFilter
|
||||||
{
|
{
|
||||||
|
|
@ -358,38 +360,38 @@ class AutoFilter
|
||||||
if (is_numeric($rule['value'])) {
|
if (is_numeric($rule['value'])) {
|
||||||
// Numeric values are tested using the appropriate operator
|
// Numeric values are tested using the appropriate operator
|
||||||
switch ($rule['operator']) {
|
switch ($rule['operator']) {
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
|
case Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
|
||||||
$retVal = ($cellValue == $rule['value']);
|
$retVal = ($cellValue == $rule['value']);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
|
case Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
|
||||||
$retVal = ($cellValue != $rule['value']);
|
$retVal = ($cellValue != $rule['value']);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN:
|
case Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN:
|
||||||
$retVal = ($cellValue > $rule['value']);
|
$retVal = ($cellValue > $rule['value']);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL:
|
case Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL:
|
||||||
$retVal = ($cellValue >= $rule['value']);
|
$retVal = ($cellValue >= $rule['value']);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN:
|
case Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN:
|
||||||
$retVal = ($cellValue < $rule['value']);
|
$retVal = ($cellValue < $rule['value']);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL:
|
case Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL:
|
||||||
$retVal = ($cellValue <= $rule['value']);
|
$retVal = ($cellValue <= $rule['value']);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} elseif ($rule['value'] == '') {
|
} elseif ($rule['value'] == '') {
|
||||||
switch ($rule['operator']) {
|
switch ($rule['operator']) {
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
|
case Rule::AUTOFILTER_COLUMN_RULE_EQUAL:
|
||||||
$retVal = (($cellValue == '') || ($cellValue === null));
|
$retVal = (($cellValue == '') || ($cellValue === null));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
|
case Rule::AUTOFILTER_COLUMN_RULE_NOTEQUAL:
|
||||||
$retVal = (($cellValue != '') && ($cellValue !== null));
|
$retVal = (($cellValue != '') && ($cellValue !== null));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -439,7 +441,8 @@ class AutoFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_numeric($cellValue)) {
|
if (is_numeric($cellValue)) {
|
||||||
$dateValue = date('m', Date::excelToTimestamp($cellValue));
|
$dateObject = Date::excelToDateTimeObject((float) $cellValue, new DateTimeZone('UTC'));
|
||||||
|
$dateValue = (int) $dateObject->format('m');
|
||||||
if (in_array($dateValue, $monthSet)) {
|
if (in_array($dateValue, $monthSet)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -457,6 +460,224 @@ class AutoFilter
|
||||||
|
|
||||||
private static $toReplace = ['.*', '.', '~', '\*', '\?'];
|
private static $toReplace = ['.*', '.', '~', '\*', '\?'];
|
||||||
|
|
||||||
|
private static function makeDateObject(int $year, int $month, int $day, int $hour = 0, int $minute = 0, int $second = 0): DateTime
|
||||||
|
{
|
||||||
|
$baseDate = new DateTime();
|
||||||
|
$baseDate->setDate($year, $month, $day);
|
||||||
|
$baseDate->setTime($hour, $minute, $second);
|
||||||
|
|
||||||
|
return $baseDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private const DATE_FUNCTIONS = [
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH => 'dynamicLastMonth',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER => 'dynamicLastQuarter',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK => 'dynamicLastWeek',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR => 'dynamicLastYear',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH => 'dynamicNextMonth',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER => 'dynamicNextQuarter',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK => 'dynamicNextWeek',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR => 'dynamicNextYear',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH => 'dynamicThisMonth',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER => 'dynamicThisQuarter',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK => 'dynamicThisWeek',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR => 'dynamicThisYear',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_TODAY => 'dynamicToday',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW => 'dynamicTomorrow',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE => 'dynamicYearToDate',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY => 'dynamicYesterday',
|
||||||
|
];
|
||||||
|
|
||||||
|
private static function dynamicLastMonth(): array
|
||||||
|
{
|
||||||
|
$maxval = new DateTime();
|
||||||
|
$year = (int) $maxval->format('Y');
|
||||||
|
$month = (int) $maxval->format('m');
|
||||||
|
$maxval->setDate($year, $month, 1);
|
||||||
|
$maxval->setTime(0, 0, 0);
|
||||||
|
$val = clone $maxval;
|
||||||
|
$val->modify('-1 month');
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function firstDayOfQuarter(): DateTime
|
||||||
|
{
|
||||||
|
$val = new DateTime();
|
||||||
|
$year = (int) $val->format('Y');
|
||||||
|
$month = (int) $val->format('m');
|
||||||
|
$month = 3 * intdiv($month - 1, 3) + 1;
|
||||||
|
$val->setDate($year, $month, 1);
|
||||||
|
$val->setTime(0, 0, 0);
|
||||||
|
|
||||||
|
return $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicLastQuarter(): array
|
||||||
|
{
|
||||||
|
$maxval = self::firstDayOfQuarter();
|
||||||
|
$val = clone $maxval;
|
||||||
|
$val->modify('-3 months');
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicLastWeek(): array
|
||||||
|
{
|
||||||
|
$val = new DateTime();
|
||||||
|
$val->setTime(0, 0, 0);
|
||||||
|
$dayOfWeek = (int) $val->format('w'); // Sunday is 0
|
||||||
|
$subtract = $dayOfWeek + 7; // revert to prior Sunday
|
||||||
|
$val->modify("-$subtract days");
|
||||||
|
$maxval = clone $val;
|
||||||
|
$maxval->modify('+7 days');
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicLastYear(): array
|
||||||
|
{
|
||||||
|
$val = new DateTime();
|
||||||
|
$year = (int) $val->format('Y');
|
||||||
|
$val = self::makeDateObject($year - 1, 1, 1);
|
||||||
|
$maxval = self::makeDateObject($year, 1, 1);
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicNextMonth(): array
|
||||||
|
{
|
||||||
|
$val = new DateTime();
|
||||||
|
$year = (int) $val->format('Y');
|
||||||
|
$month = (int) $val->format('m');
|
||||||
|
$val->setDate($year, $month, 1);
|
||||||
|
$val->setTime(0, 0, 0);
|
||||||
|
$val->modify('+1 month');
|
||||||
|
$maxval = clone $val;
|
||||||
|
$maxval->modify('+1 month');
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicNextQuarter(): array
|
||||||
|
{
|
||||||
|
$val = self::firstDayOfQuarter();
|
||||||
|
$val->modify('+3 months');
|
||||||
|
$maxval = clone $val;
|
||||||
|
$maxval->modify('+3 months');
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicNextWeek(): array
|
||||||
|
{
|
||||||
|
$val = new DateTime();
|
||||||
|
$val->setTime(0, 0, 0);
|
||||||
|
$dayOfWeek = (int) $val->format('w'); // Sunday is 0
|
||||||
|
$add = 7 - $dayOfWeek; // move to next Sunday
|
||||||
|
$val->modify("+$add days");
|
||||||
|
$maxval = clone $val;
|
||||||
|
$maxval->modify('+7 days');
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicNextYear(): array
|
||||||
|
{
|
||||||
|
$val = new DateTime();
|
||||||
|
$year = (int) $val->format('Y');
|
||||||
|
$val = self::makeDateObject($year + 1, 1, 1);
|
||||||
|
$maxval = self::makeDateObject($year + 2, 1, 1);
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicThisMonth(): array
|
||||||
|
{
|
||||||
|
$baseDate = new DateTime();
|
||||||
|
$baseDate->setTime(0, 0, 0);
|
||||||
|
$year = (int) $baseDate->format('Y');
|
||||||
|
$month = (int) $baseDate->format('m');
|
||||||
|
$val = self::makeDateObject($year, $month, 1);
|
||||||
|
$maxval = clone $val;
|
||||||
|
$maxval->modify('+1 month');
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicThisQuarter(): array
|
||||||
|
{
|
||||||
|
$val = self::firstDayOfQuarter();
|
||||||
|
$maxval = clone $val;
|
||||||
|
$maxval->modify('+3 months');
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicThisWeek(): array
|
||||||
|
{
|
||||||
|
$val = new DateTime();
|
||||||
|
$val->setTime(0, 0, 0);
|
||||||
|
$dayOfWeek = (int) $val->format('w'); // Sunday is 0
|
||||||
|
$subtract = $dayOfWeek; // revert to Sunday
|
||||||
|
$val->modify("-$subtract days");
|
||||||
|
$maxval = clone $val;
|
||||||
|
$maxval->modify('+7 days');
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicThisYear(): array
|
||||||
|
{
|
||||||
|
$val = new DateTime();
|
||||||
|
$year = (int) $val->format('Y');
|
||||||
|
$val = self::makeDateObject($year, 1, 1);
|
||||||
|
$maxval = self::makeDateObject($year + 1, 1, 1);
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicToday(): array
|
||||||
|
{
|
||||||
|
$val = new DateTime();
|
||||||
|
$val->setTime(0, 0, 0);
|
||||||
|
$maxval = clone $val;
|
||||||
|
$maxval->modify('+1 day');
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicTomorrow(): array
|
||||||
|
{
|
||||||
|
$val = new DateTime();
|
||||||
|
$val->setTime(0, 0, 0);
|
||||||
|
$val->modify('+1 day');
|
||||||
|
$maxval = clone $val;
|
||||||
|
$maxval->modify('+1 day');
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicYearToDate(): array
|
||||||
|
{
|
||||||
|
$maxval = new DateTime();
|
||||||
|
$maxval->setTime(0, 0, 0);
|
||||||
|
$val = self::makeDateObject((int) $maxval->format('Y'), 1, 1);
|
||||||
|
$maxval->modify('+1 day');
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function dynamicYesterday(): array
|
||||||
|
{
|
||||||
|
$maxval = new DateTime();
|
||||||
|
$maxval->setTime(0, 0, 0);
|
||||||
|
$val = clone $maxval;
|
||||||
|
$val->modify('-1 day');
|
||||||
|
|
||||||
|
return [$val, $maxval];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a dynamic rule daterange to a custom filter range expression for ease of calculation.
|
* Convert a dynamic rule daterange to a custom filter range expression for ease of calculation.
|
||||||
*
|
*
|
||||||
|
|
@ -467,118 +688,25 @@ class AutoFilter
|
||||||
*/
|
*/
|
||||||
private function dynamicFilterDateRange($dynamicRuleType, &$filterColumn)
|
private function dynamicFilterDateRange($dynamicRuleType, &$filterColumn)
|
||||||
{
|
{
|
||||||
$rDateType = Functions::getReturnDateType();
|
|
||||||
Functions::setReturnDateType(Functions::RETURNDATE_PHP_NUMERIC);
|
|
||||||
$val = $maxVal = null;
|
|
||||||
|
|
||||||
$ruleValues = [];
|
$ruleValues = [];
|
||||||
$baseDate = DateTimeExcel\Current::now();
|
$val = $maxVal = null;
|
||||||
|
$callBack = [__CLASS__, self::DATE_FUNCTIONS[$dynamicRuleType]]; // What if not found?
|
||||||
// Calculate start/end dates for the required date range based on current date
|
// Calculate start/end dates for the required date range based on current date
|
||||||
switch ($dynamicRuleType) {
|
// Val is lowest permitted value.
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK:
|
// Maxval is greater than highest permitted value
|
||||||
$baseDate = strtotime('-7 days', $baseDate);
|
$val = $maxval = 0;
|
||||||
|
if (is_callable($callBack)) {
|
||||||
break;
|
[$val, $maxval] = $callBack();
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK:
|
|
||||||
$baseDate = strtotime('-7 days', $baseDate);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH:
|
|
||||||
$baseDate = strtotime('-1 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
|
|
||||||
|
|
||||||
break;
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH:
|
|
||||||
$baseDate = strtotime('+1 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
|
|
||||||
|
|
||||||
break;
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER:
|
|
||||||
$baseDate = strtotime('-3 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
|
|
||||||
|
|
||||||
break;
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER:
|
|
||||||
$baseDate = strtotime('+3 month', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
|
|
||||||
|
|
||||||
break;
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR:
|
|
||||||
$baseDate = strtotime('-1 year', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
|
|
||||||
|
|
||||||
break;
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR:
|
|
||||||
$baseDate = strtotime('+1 year', gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($dynamicRuleType) {
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_TODAY:
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY:
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW:
|
|
||||||
$maxVal = (int) Date::PHPtoExcel(strtotime('+1 day', $baseDate));
|
|
||||||
$val = (int) Date::PHPToExcel($baseDate);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE:
|
|
||||||
$maxVal = (int) Date::PHPtoExcel(strtotime('+1 day', $baseDate));
|
|
||||||
$val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1, date('Y', $baseDate)));
|
|
||||||
|
|
||||||
break;
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR:
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR:
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR:
|
|
||||||
$maxVal = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 31, 12, date('Y', $baseDate)));
|
|
||||||
++$maxVal;
|
|
||||||
$val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1, date('Y', $baseDate)));
|
|
||||||
|
|
||||||
break;
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER:
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER:
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER:
|
|
||||||
$thisMonth = date('m', $baseDate);
|
|
||||||
$thisQuarter = floor(--$thisMonth / 3);
|
|
||||||
$maxVal = (int) Date::PHPtoExcel(gmmktime(0, 0, 0, date('t', $baseDate), (1 + $thisQuarter) * 3, date('Y', $baseDate)));
|
|
||||||
++$maxVal;
|
|
||||||
$val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, 1 + $thisQuarter * 3, date('Y', $baseDate)));
|
|
||||||
|
|
||||||
break;
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH:
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH:
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH:
|
|
||||||
$maxVal = (int) Date::PHPtoExcel(gmmktime(0, 0, 0, date('t', $baseDate), date('m', $baseDate), date('Y', $baseDate)));
|
|
||||||
++$maxVal;
|
|
||||||
$val = (int) Date::PHPToExcel(gmmktime(0, 0, 0, 1, date('m', $baseDate), date('Y', $baseDate)));
|
|
||||||
|
|
||||||
break;
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK:
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK:
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK:
|
|
||||||
$dayOfWeek = date('w', $baseDate);
|
|
||||||
$val = (int) Date::PHPToExcel($baseDate) - $dayOfWeek;
|
|
||||||
$maxVal = $val + 7;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($dynamicRuleType) {
|
|
||||||
// Adjust Today dates for Yesterday and Tomorrow
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY:
|
|
||||||
--$maxVal;
|
|
||||||
--$val;
|
|
||||||
|
|
||||||
break;
|
|
||||||
case AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW:
|
|
||||||
++$maxVal;
|
|
||||||
++$val;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
$val = Date::dateTimeToExcel($val);
|
||||||
|
$maxval = Date::dateTimeToExcel($maxval);
|
||||||
|
|
||||||
// Set the filter column rule attributes ready for writing
|
// Set the filter column rule attributes ready for writing
|
||||||
$filterColumn->setAttributes(['val' => $val, 'maxVal' => $maxVal]);
|
$filterColumn->setAttributes(['val' => $val, 'maxVal' => $maxval]);
|
||||||
|
|
||||||
// Set the rules for identifying rows for hide/show
|
// Set the rules for identifying rows for hide/show
|
||||||
$ruleValues[] = ['operator' => AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL, 'value' => $val];
|
$ruleValues[] = ['operator' => Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL, 'value' => $val];
|
||||||
$ruleValues[] = ['operator' => AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN, 'value' => $maxVal];
|
$ruleValues[] = ['operator' => Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN, 'value' => $maxval];
|
||||||
Functions::setReturnDateType($rDateType);
|
|
||||||
|
|
||||||
return ['method' => 'filterTestInCustomDataSet', 'arguments' => ['filterRules' => $ruleValues, 'join' => AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND]];
|
return ['method' => 'filterTestInCustomDataSet', 'arguments' => ['filterRules' => $ruleValues, 'join' => AutoFilter\Column::AUTOFILTER_COLUMN_JOIN_AND]];
|
||||||
}
|
}
|
||||||
|
|
@ -589,7 +717,7 @@ class AutoFilter
|
||||||
$dataValues = Functions::flattenArray($this->workSheet->rangeToArray($range, null, true, false));
|
$dataValues = Functions::flattenArray($this->workSheet->rangeToArray($range, null, true, false));
|
||||||
|
|
||||||
$dataValues = array_filter($dataValues);
|
$dataValues = array_filter($dataValues);
|
||||||
if ($ruleType == AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP) {
|
if ($ruleType == Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP) {
|
||||||
rsort($dataValues);
|
rsort($dataValues);
|
||||||
} else {
|
} else {
|
||||||
sort($dataValues);
|
sort($dataValues);
|
||||||
|
|
@ -630,7 +758,7 @@ class AutoFilter
|
||||||
if (count($ruleValues) != count($ruleDataSet)) {
|
if (count($ruleValues) != count($ruleDataSet)) {
|
||||||
$blanks = true;
|
$blanks = true;
|
||||||
}
|
}
|
||||||
if ($ruleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_FILTER) {
|
if ($ruleType == Rule::AUTOFILTER_RULETYPE_FILTER) {
|
||||||
// Filter on absolute values
|
// Filter on absolute values
|
||||||
$columnFilterTests[$columnID] = [
|
$columnFilterTests[$columnID] = [
|
||||||
'method' => 'filterTestInSimpleDataSet',
|
'method' => 'filterTestInSimpleDataSet',
|
||||||
|
|
@ -646,40 +774,40 @@ class AutoFilter
|
||||||
foreach ($ruleDataSet as $ruleValue) {
|
foreach ($ruleDataSet as $ruleValue) {
|
||||||
$date = $time = '';
|
$date = $time = '';
|
||||||
if (
|
if (
|
||||||
(isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR])) &&
|
(isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR])) &&
|
||||||
($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR] !== '')
|
($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR] !== '')
|
||||||
) {
|
) {
|
||||||
$date .= sprintf('%04d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR]);
|
$date .= sprintf('%04d', $ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_YEAR]);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
(isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH])) &&
|
(isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH])) &&
|
||||||
($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH] != '')
|
($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH] != '')
|
||||||
) {
|
) {
|
||||||
$date .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH]);
|
$date .= sprintf('%02d', $ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MONTH]);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
(isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY])) &&
|
(isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY])) &&
|
||||||
($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY] !== '')
|
($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY] !== '')
|
||||||
) {
|
) {
|
||||||
$date .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY]);
|
$date .= sprintf('%02d', $ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_DAY]);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
(isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR])) &&
|
(isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR])) &&
|
||||||
($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR] !== '')
|
($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR] !== '')
|
||||||
) {
|
) {
|
||||||
$time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR]);
|
$time .= sprintf('%02d', $ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_HOUR]);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
(isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE])) &&
|
(isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE])) &&
|
||||||
($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE] !== '')
|
($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE] !== '')
|
||||||
) {
|
) {
|
||||||
$time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE]);
|
$time .= sprintf('%02d', $ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_MINUTE]);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
(isset($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND])) &&
|
(isset($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND])) &&
|
||||||
($ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND] !== '')
|
($ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND] !== '')
|
||||||
) {
|
) {
|
||||||
$time .= sprintf('%02d', $ruleValue[AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND]);
|
$time .= sprintf('%02d', $ruleValue[Rule::AUTOFILTER_RULETYPE_DATEGROUP_SECOND]);
|
||||||
}
|
}
|
||||||
$dateTime = $date . $time;
|
$dateTime = $date . $time;
|
||||||
$arguments['date'][] = $date;
|
$arguments['date'][] = $date;
|
||||||
|
|
@ -727,17 +855,17 @@ class AutoFilter
|
||||||
// We should only ever have one Dynamic Filter Rule anyway
|
// We should only ever have one Dynamic Filter Rule anyway
|
||||||
$dynamicRuleType = $rule->getGrouping();
|
$dynamicRuleType = $rule->getGrouping();
|
||||||
if (
|
if (
|
||||||
($dynamicRuleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE) ||
|
($dynamicRuleType == Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE) ||
|
||||||
($dynamicRuleType == AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE)
|
($dynamicRuleType == Rule::AUTOFILTER_RULETYPE_DYNAMIC_BELOWAVERAGE)
|
||||||
) {
|
) {
|
||||||
// Number (Average) based
|
// Number (Average) based
|
||||||
// Calculate the average
|
// Calculate the average
|
||||||
$averageFormula = '=AVERAGE(' . $columnID . ($rangeStart[1] + 1) . ':' . $columnID . $rangeEnd[1] . ')';
|
$averageFormula = '=AVERAGE(' . $columnID . ($rangeStart[1] + 1) . ':' . $columnID . $rangeEnd[1] . ')';
|
||||||
$average = Calculation::getInstance()->calculateFormula($averageFormula, null, $this->workSheet->getCell('A1'));
|
$average = Calculation::getInstance()->calculateFormula($averageFormula, null, $this->workSheet->getCell('A1'));
|
||||||
// Set above/below rule based on greaterThan or LessTan
|
// Set above/below rule based on greaterThan or LessTan
|
||||||
$operator = ($dynamicRuleType === AutoFilter\Column\Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE)
|
$operator = ($dynamicRuleType === Rule::AUTOFILTER_RULETYPE_DYNAMIC_ABOVEAVERAGE)
|
||||||
? AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN
|
? Rule::AUTOFILTER_COLUMN_RULE_GREATERTHAN
|
||||||
: AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN;
|
: Rule::AUTOFILTER_COLUMN_RULE_LESSTHAN;
|
||||||
$ruleValues[] = [
|
$ruleValues[] = [
|
||||||
'operator' => $operator,
|
'operator' => $operator,
|
||||||
'value' => $average,
|
'value' => $average,
|
||||||
|
|
@ -788,7 +916,7 @@ class AutoFilter
|
||||||
$ruleValue = $rule->getValue();
|
$ruleValue = $rule->getValue();
|
||||||
$ruleOperator = $rule->getOperator();
|
$ruleOperator = $rule->getOperator();
|
||||||
}
|
}
|
||||||
if ($ruleOperator === AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT) {
|
if ($ruleOperator === Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_PERCENT) {
|
||||||
$ruleValue = floor($ruleValue * ($dataRowCount / 100));
|
$ruleValue = floor($ruleValue * ($dataRowCount / 100));
|
||||||
}
|
}
|
||||||
if (!is_array($ruleValue) && $ruleValue < 1) {
|
if (!is_array($ruleValue) && $ruleValue < 1) {
|
||||||
|
|
@ -800,9 +928,9 @@ class AutoFilter
|
||||||
|
|
||||||
$maxVal = $this->calculateTopTenValue($columnID, $rangeStart[1] + 1, $rangeEnd[1], $toptenRuleType, $ruleValue);
|
$maxVal = $this->calculateTopTenValue($columnID, $rangeStart[1] + 1, $rangeEnd[1], $toptenRuleType, $ruleValue);
|
||||||
|
|
||||||
$operator = ($toptenRuleType == AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP)
|
$operator = ($toptenRuleType == Rule::AUTOFILTER_COLUMN_RULE_TOPTEN_TOP)
|
||||||
? AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL
|
? Rule::AUTOFILTER_COLUMN_RULE_GREATERTHANOREQUAL
|
||||||
: AutoFilter\Column\Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL;
|
: Rule::AUTOFILTER_COLUMN_RULE_LESSTHANOREQUAL;
|
||||||
$ruleValues[] = ['operator' => $operator, 'value' => $maxVal];
|
$ruleValues[] = ['operator' => $operator, 'value' => $maxVal];
|
||||||
$columnFilterTests[$columnID] = [
|
$columnFilterTests[$columnID] = [
|
||||||
'method' => 'filterTestInCustomDataSet',
|
'method' => 'filterTestInCustomDataSet',
|
||||||
|
|
|
||||||
|
|
@ -215,11 +215,11 @@ class Column
|
||||||
/**
|
/**
|
||||||
* Set AutoFilter Attributes.
|
* Set AutoFilter Attributes.
|
||||||
*
|
*
|
||||||
* @param string[] $attributes
|
* @param mixed[] $attributes
|
||||||
*
|
*
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setAttributes(array $attributes)
|
public function setAttributes($attributes)
|
||||||
{
|
{
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class AutoFilterMonthTest extends TestCase
|
||||||
|
{
|
||||||
|
public function providerMonth(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[[2, 3], Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISMONTH],
|
||||||
|
[[4], Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTMONTH],
|
||||||
|
[[6], Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTMONTH],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function setCells(Worksheet $sheet, int $startMonth): void
|
||||||
|
{
|
||||||
|
$sheet->getCell('A1')->setValue('Date');
|
||||||
|
$sheet->getCell('A2')->setValue('=TODAY()');
|
||||||
|
$sheet->getCell('A3')->setValue('=DATE(YEAR(A2), MONTH(A2), 1)');
|
||||||
|
if ($startMonth === 12) {
|
||||||
|
$sheet->getCell('A4')->setValue('=DATE(YEAR(A2) + 1, 1, 1)');
|
||||||
|
$sheet->getCell('A5')->setValue('=DATE(YEAR(A2) + 1, 2, 1)');
|
||||||
|
} elseif ($startMonth === 11) {
|
||||||
|
$sheet->getCell('A4')->setValue('=DATE(YEAR(A2), MONTH(A2) + 1, 1)');
|
||||||
|
$sheet->getCell('A5')->setValue('=DATE(YEAR(A2) + 1, 1, 1)');
|
||||||
|
} else {
|
||||||
|
$sheet->getCell('A4')->setValue('=DATE(YEAR(A2), MONTH(A2) + 1, 1)');
|
||||||
|
$sheet->getCell('A5')->setValue('=DATE(YEAR(A2), MONTH(A2) + 2, 1)');
|
||||||
|
}
|
||||||
|
if ($startMonth === 1) {
|
||||||
|
$sheet->getCell('A6')->setValue('=DATE(YEAR(A2) - 1, 12, 1)');
|
||||||
|
$sheet->getCell('A7')->setValue('=DATE(YEAR(A2) - 1, 10, 1)');
|
||||||
|
} elseif ($startMonth === 2) {
|
||||||
|
$sheet->getCell('A6')->setValue('=DATE(YEAR(A2), 1, 1)');
|
||||||
|
$sheet->getCell('A7')->setValue('=DATE(YEAR(A2) - 1, 12, 1)');
|
||||||
|
} else {
|
||||||
|
$sheet->getCell('A6')->setValue('=DATE(YEAR(A2), MONTH(A2) - 1, 1)');
|
||||||
|
$sheet->getCell('A7')->setValue('=DATE(YEAR(A2), MONTH(A2) - 2, 1)');
|
||||||
|
}
|
||||||
|
$sheet->getCell('A8')->setValue('=DATE(YEAR(A2) + 1, MONTH(A2), 1)');
|
||||||
|
$sheet->getCell('A9')->setValue('=DATE(YEAR(A2) - 1, MONTH(A2), 1)');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providerMonth
|
||||||
|
*/
|
||||||
|
public function testMonths(array $expectedVisible, string $rule): void
|
||||||
|
{
|
||||||
|
// Loop to avoid rare edge case where first calculation
|
||||||
|
// and second do not take place in same day.
|
||||||
|
do {
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$sheet = $spreadsheet->getActiveSheet();
|
||||||
|
$dtStart = new DateTimeImmutable();
|
||||||
|
$startDay = (int) $dtStart->format('d');
|
||||||
|
$startMonth = (int) $dtStart->format('m');
|
||||||
|
self::setCells($sheet, $startMonth);
|
||||||
|
|
||||||
|
$maxRow = 9;
|
||||||
|
$autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter();
|
||||||
|
$autoFilter->setRange("A1:A$maxRow");
|
||||||
|
$columnFilter = $autoFilter->getColumn('A');
|
||||||
|
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
|
||||||
|
$columnFilter->createRule()
|
||||||
|
->setRule(
|
||||||
|
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
|
||||||
|
'',
|
||||||
|
$rule
|
||||||
|
)
|
||||||
|
->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
|
||||||
|
$autoFilter->showHideRows();
|
||||||
|
$dtEnd = new DateTimeImmutable();
|
||||||
|
$endDay = (int) $dtEnd->format('d');
|
||||||
|
} while ($startDay !== $endDay);
|
||||||
|
$actualVisible = [];
|
||||||
|
for ($row = 2; $row <= $maxRow; ++$row) {
|
||||||
|
if ($sheet->getRowDimension($row)->getVisible()) {
|
||||||
|
$actualVisible[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self::assertEquals($expectedVisible, $actualVisible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class AutoFilterQuarterTest extends TestCase
|
||||||
|
{
|
||||||
|
public function providerQuarter(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[[2, 3], Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISQUARTER],
|
||||||
|
[[4], Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTQUARTER],
|
||||||
|
[[6], Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTQUARTER],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function setCells(Worksheet $sheet, int $startMonth): void
|
||||||
|
{
|
||||||
|
$sheet->getCell('A1')->setValue('Date');
|
||||||
|
$sheet->getCell('A2')->setValue('=TODAY()');
|
||||||
|
$sheet->getCell('A3')->setValue('=DATE(YEAR(A2), MONTH(A2), 1)');
|
||||||
|
$sheet->getCell('A4')->setValue('=DATE(YEAR(A2), MONTH(A2) + 3, 1)');
|
||||||
|
$sheet->getCell('A5')->setValue('=DATE(YEAR(A2), MONTH(A2) + 6, 1)');
|
||||||
|
$sheet->getCell('A6')->setValue('=DATE(YEAR(A2), MONTH(A2) - 3, 1)');
|
||||||
|
$sheet->getCell('A7')->setValue('=DATE(YEAR(A2), MONTH(A2) - 6, 1)');
|
||||||
|
$sheet->getCell('A8')->setValue('=DATE(YEAR(A2) + 1, MONTH(A2), 1)');
|
||||||
|
$sheet->getCell('A9')->setValue('=DATE(YEAR(A2) - 1, MONTH(A2), 1)');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providerQuarter
|
||||||
|
*/
|
||||||
|
public function testQuarters(array $expectedVisible, string $rule): void
|
||||||
|
{
|
||||||
|
// Loop to avoid rare edge case where first calculation
|
||||||
|
// and second do not take place in same day.
|
||||||
|
do {
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$sheet = $spreadsheet->getActiveSheet();
|
||||||
|
$dtStart = new DateTimeImmutable();
|
||||||
|
$startDay = (int) $dtStart->format('d');
|
||||||
|
$startMonth = (int) $dtStart->format('m');
|
||||||
|
self::setCells($sheet, $startMonth);
|
||||||
|
|
||||||
|
$maxRow = 9;
|
||||||
|
$autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter();
|
||||||
|
$autoFilter->setRange("A1:A$maxRow");
|
||||||
|
$columnFilter = $autoFilter->getColumn('A');
|
||||||
|
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
|
||||||
|
$columnFilter->createRule()
|
||||||
|
->setRule(
|
||||||
|
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
|
||||||
|
'',
|
||||||
|
$rule
|
||||||
|
)
|
||||||
|
->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
|
||||||
|
$autoFilter->showHideRows();
|
||||||
|
$dtEnd = new DateTimeImmutable();
|
||||||
|
$endDay = (int) $dtEnd->format('d');
|
||||||
|
} while ($startDay !== $endDay);
|
||||||
|
$actualVisible = [];
|
||||||
|
for ($row = 2; $row <= $maxRow; ++$row) {
|
||||||
|
if ($sheet->getRowDimension($row)->getVisible()) {
|
||||||
|
$actualVisible[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self::assertEquals($expectedVisible, $actualVisible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace PhpOffice\PhpSpreadsheetTests\Worksheet;
|
namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter;
|
||||||
|
|
||||||
use PhpOffice\PhpSpreadsheet\Collection\Cells;
|
use PhpOffice\PhpSpreadsheet\Collection\Cells;
|
||||||
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter;
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter;
|
||||||
|
|
@ -207,6 +207,7 @@ class AutoFilterTest extends TestCase
|
||||||
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
|
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
|
||||||
|
|
||||||
$invalidColumn = 123.456;
|
$invalidColumn = 123.456;
|
||||||
|
// @phpstan-ignore-next-line
|
||||||
$this->testAutoFilterObject->setColumn($invalidColumn);
|
$this->testAutoFilterObject->setColumn($invalidColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class AutoFilterTodayTest extends TestCase
|
||||||
|
{
|
||||||
|
public function providerYesterdayTodayTomorrow(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[[2, 5], Rule::AUTOFILTER_RULETYPE_DYNAMIC_TODAY],
|
||||||
|
[[3, 6], Rule::AUTOFILTER_RULETYPE_DYNAMIC_TOMORROW],
|
||||||
|
[[4, 7], Rule::AUTOFILTER_RULETYPE_DYNAMIC_YESTERDAY],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providerYesterdayTodayTomorrow
|
||||||
|
*/
|
||||||
|
public function testYesterdayTodayTomorrow(array $expectedVisible, string $rule): void
|
||||||
|
{
|
||||||
|
// Loop to avoid rare edge case where first calculation
|
||||||
|
// and second do not take place in same day.
|
||||||
|
do {
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$sheet = $spreadsheet->getActiveSheet();
|
||||||
|
$dtStart = new DateTimeImmutable();
|
||||||
|
$startDay = $dtStart->format('d');
|
||||||
|
$sheet->getCell('A1')->setValue('Date');
|
||||||
|
$sheet->getCell('A2')->setValue('=NOW()');
|
||||||
|
$sheet->getCell('A3')->setValue('=A2+1');
|
||||||
|
$sheet->getCell('A4')->setValue('=A2-1');
|
||||||
|
$sheet->getCell('A5')->setValue('=TODAY()');
|
||||||
|
$sheet->getCell('A6')->setValue('=A5+1');
|
||||||
|
$sheet->getCell('A7')->setValue('=A5-1');
|
||||||
|
$maxRow = 7;
|
||||||
|
$autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter();
|
||||||
|
$autoFilter->setRange("A1:A$maxRow");
|
||||||
|
$columnFilter = $autoFilter->getColumn('A');
|
||||||
|
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
|
||||||
|
$columnFilter->createRule()
|
||||||
|
->setRule(
|
||||||
|
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
|
||||||
|
'',
|
||||||
|
$rule
|
||||||
|
)
|
||||||
|
->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
|
||||||
|
$autoFilter->showHideRows();
|
||||||
|
$dtEnd = new DateTimeImmutable();
|
||||||
|
$endDay = $dtEnd->format('d');
|
||||||
|
} while ($startDay !== $endDay);
|
||||||
|
$actualVisible = [];
|
||||||
|
for ($row = 2; $row <= $maxRow; ++$row) {
|
||||||
|
if ($sheet->getRowDimension($row)->getVisible()) {
|
||||||
|
$actualVisible[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self::assertEquals($expectedVisible, $actualVisible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class AutoFilterWeekTest extends TestCase
|
||||||
|
{
|
||||||
|
public function providerWeek(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[[2, 3], Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISWEEK],
|
||||||
|
[[4], Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTWEEK],
|
||||||
|
[[6], Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTWEEK],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function setCells(Worksheet $sheet): void
|
||||||
|
{
|
||||||
|
$sheet->getCell('A1')->setValue('Date');
|
||||||
|
$sheet->getCell('A2')->setValue('=TODAY()');
|
||||||
|
$sheet->getCell('B2')->setValue('=WEEKDAY(A2) - 1'); // subtract to get to Sunday
|
||||||
|
$sheet->getCell('A3')->setValue('=DATE(YEAR(A2), MONTH(A2), DAY(A2) - B2)');
|
||||||
|
$sheet->getCell('A4')->setValue('=DATE(YEAR(A3), MONTH(A3), DAY(A3) + 8)');
|
||||||
|
$sheet->getCell('A5')->setValue('=DATE(YEAR(A3), MONTH(A3), DAY(A3) + 19)');
|
||||||
|
$sheet->getCell('A6')->setValue('=DATE(YEAR(A3), MONTH(A3), DAY(A3) - 6)');
|
||||||
|
$sheet->getCell('A7')->setValue('=DATE(YEAR(A3), MONTH(A3), DAY(A3) - 12)');
|
||||||
|
$sheet->getCell('A8')->setValue('=DATE(YEAR(A2) + 1, MONTH(A2), 1)');
|
||||||
|
$sheet->getCell('A9')->setValue('=DATE(YEAR(A2) - 1, MONTH(A2), 1)');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providerWeek
|
||||||
|
*/
|
||||||
|
public function testWeek(array $expectedVisible, string $rule): void
|
||||||
|
{
|
||||||
|
// Loop to avoid rare edge case where first calculation
|
||||||
|
// and second do not take place in same day.
|
||||||
|
do {
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$sheet = $spreadsheet->getActiveSheet();
|
||||||
|
$dtStart = new DateTimeImmutable();
|
||||||
|
$startDay = (int) $dtStart->format('d');
|
||||||
|
$startMonth = (int) $dtStart->format('m');
|
||||||
|
self::setCells($sheet);
|
||||||
|
|
||||||
|
$maxRow = 9;
|
||||||
|
$autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter();
|
||||||
|
$autoFilter->setRange("A1:A$maxRow");
|
||||||
|
$columnFilter = $autoFilter->getColumn('A');
|
||||||
|
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
|
||||||
|
$columnFilter->createRule()
|
||||||
|
->setRule(
|
||||||
|
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
|
||||||
|
'',
|
||||||
|
$rule
|
||||||
|
)
|
||||||
|
->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
|
||||||
|
$autoFilter->showHideRows();
|
||||||
|
$dtEnd = new DateTimeImmutable();
|
||||||
|
$endDay = (int) $dtEnd->format('d');
|
||||||
|
} while ($startDay !== $endDay);
|
||||||
|
$actualVisible = [];
|
||||||
|
for ($row = 2; $row <= $maxRow; ++$row) {
|
||||||
|
if ($sheet->getRowDimension($row)->getVisible()) {
|
||||||
|
$actualVisible[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self::assertEquals($expectedVisible, $actualVisible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PhpOffice\PhpSpreadsheetTests\Worksheet\AutoFilter;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Worksheet\AutoFilter\Column\Rule;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class AutoFilterYearTest extends TestCase
|
||||||
|
{
|
||||||
|
public function providerYear(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[[5, 6, 7], Rule::AUTOFILTER_RULETYPE_DYNAMIC_THISYEAR],
|
||||||
|
[[2, 3, 4], Rule::AUTOFILTER_RULETYPE_DYNAMIC_LASTYEAR],
|
||||||
|
[[8, 9, 10], Rule::AUTOFILTER_RULETYPE_DYNAMIC_NEXTYEAR],
|
||||||
|
[[], Rule::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_2],
|
||||||
|
[[2, 5, 8, 11], Rule::AUTOFILTER_RULETYPE_DYNAMIC_QUARTER_1],
|
||||||
|
[[4, 7, 10], Rule::AUTOFILTER_RULETYPE_DYNAMIC_MONTH_11],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider providerYear
|
||||||
|
*/
|
||||||
|
public function testYears(array $expectedVisible, string $rule): void
|
||||||
|
{
|
||||||
|
// Loop to avoid rare edge case where first calculation
|
||||||
|
// and second do not take place in same day.
|
||||||
|
do {
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$sheet = $spreadsheet->getActiveSheet();
|
||||||
|
$dtStart = new DateTimeImmutable();
|
||||||
|
$startDay = (int) $dtStart->format('d');
|
||||||
|
$sheet->getCell('A1')->setValue('Date');
|
||||||
|
$year = (int) $dtStart->format('Y') - 1;
|
||||||
|
$row = 1;
|
||||||
|
$iteration = 0;
|
||||||
|
while ($iteration < 3) {
|
||||||
|
for ($month = 3; $month < 13; $month += 4) {
|
||||||
|
++$row;
|
||||||
|
$sheet->getCell("A$row")->setValue("=DATE($year, $month, 1)");
|
||||||
|
}
|
||||||
|
++$year;
|
||||||
|
++$iteration;
|
||||||
|
}
|
||||||
|
++$row;
|
||||||
|
$sheet->getCell("A$row")->setValue('=DATE(2041, 1, 1)'); // beyond epoch
|
||||||
|
$maxRow = $row;
|
||||||
|
$autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter();
|
||||||
|
$autoFilter->setRange("A1:A$maxRow");
|
||||||
|
$columnFilter = $autoFilter->getColumn('A');
|
||||||
|
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
|
||||||
|
$columnFilter->createRule()
|
||||||
|
->setRule(
|
||||||
|
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
|
||||||
|
'',
|
||||||
|
$rule
|
||||||
|
)
|
||||||
|
->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
|
||||||
|
$autoFilter->showHideRows();
|
||||||
|
$dtEnd = new DateTimeImmutable();
|
||||||
|
$endDay = (int) $dtEnd->format('d');
|
||||||
|
} while ($startDay !== $endDay);
|
||||||
|
$actualVisible = [];
|
||||||
|
for ($row = 2; $row <= $maxRow; ++$row) {
|
||||||
|
if ($sheet->getRowDimension($row)->getVisible()) {
|
||||||
|
$actualVisible[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self::assertEquals($expectedVisible, $actualVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testYearToDate(): void
|
||||||
|
{
|
||||||
|
// Loop to avoid rare edge case where first calculation
|
||||||
|
// and second do not take place in same day.
|
||||||
|
do {
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$sheet = $spreadsheet->getActiveSheet();
|
||||||
|
$dtStart = new DateTimeImmutable();
|
||||||
|
$startDay = (int) $dtStart->format('d');
|
||||||
|
$startMonth = (int) $dtStart->format('m');
|
||||||
|
$sheet->getCell('A1')->setValue('Date');
|
||||||
|
$year = (int) $dtStart->format('Y');
|
||||||
|
$sheet->getCell('A2')->setValue('=TODAY()');
|
||||||
|
$sheet->getCell('A3')->setValue('=DATE(YEAR(A2), 12, 31)');
|
||||||
|
$sheet->getCell('A4')->setValue('=A3 + 1');
|
||||||
|
$sheet->getCell('A5')->setValue('=DATE(YEAR(A2), 1, 1)');
|
||||||
|
$sheet->getCell('A6')->setValue('=A5 - 1');
|
||||||
|
|
||||||
|
$maxRow = 6;
|
||||||
|
$autoFilter = $spreadsheet->getActiveSheet()->getAutoFilter();
|
||||||
|
$autoFilter->setRange("A1:A$maxRow");
|
||||||
|
$columnFilter = $autoFilter->getColumn('A');
|
||||||
|
$columnFilter->setFilterType(Column::AUTOFILTER_FILTERTYPE_DYNAMICFILTER);
|
||||||
|
$columnFilter->createRule()
|
||||||
|
->setRule(
|
||||||
|
Rule::AUTOFILTER_COLUMN_RULE_EQUAL,
|
||||||
|
'',
|
||||||
|
Rule::AUTOFILTER_RULETYPE_DYNAMIC_YEARTODATE
|
||||||
|
)
|
||||||
|
->setRuleType(Rule::AUTOFILTER_RULETYPE_DYNAMICFILTER);
|
||||||
|
$autoFilter->showHideRows();
|
||||||
|
$dtEnd = new DateTimeImmutable();
|
||||||
|
$endDay = (int) $dtEnd->format('d');
|
||||||
|
} while ($startDay !== $endDay);
|
||||||
|
$actualVisible = [];
|
||||||
|
for ($row = 2; $row <= $maxRow; ++$row) {
|
||||||
|
if ($sheet->getRowDimension($row)->getVisible()) {
|
||||||
|
$actualVisible[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$expected = ($startMonth === 12 && $startDay === 31) ? [2, 3, 5] : [2, 5];
|
||||||
|
self::assertEquals($expected, $actualVisible);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue