diff --git a/README.md b/README.md index 9bae563..c29c50d 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ The default for this package is **exclusive** approach, which means when you for instance query for the last 7 days it will **not include** the current day! You can change the default if you need in the published config file. -### Config +### Global configuration You can publish the config file with: @@ -55,7 +55,7 @@ return [ /** * If you want to include the current day/week/month/year etc. in the range, * you could use the inclusive range here as a default. - * Note that you can also optionally specify it for quite every scope we offer + * Note that you can also fluently specify the range for quite every scope we offer * directly when using the scope: * Transaction::ofLast7Days(DateRange::INCLUSIVE); (this works for all but the singular "ofLast"-scopes) * This will do an inclusive query, even though the global default range here is set to exclusive. @@ -71,6 +71,20 @@ return [ If you want to change the default range to inclusive set `DATE_SCOPES_DEFAULT_RANGE=inclusive` in your `.env`. +### Fluent configuration + +As already mentioned above in the `default_range` config description text, +you can also fluently specify the range for quite every scope we offer +directly when using the scope: + +```php +// This works for all "ofLast"-scopes, expect the singulars like "ofLastHour", +// because it would not make sense for those. +Transaction::ofLast7Days(DateRange::INCLUSIVE); +``` + +This will do an inclusive query (today-6 days), even though the global default range here was set to exclusive. + ## Scopes - [`seconds`](#seconds) diff --git a/config/date-scopes.php b/config/date-scopes.php index 5c94f51..71593be 100644 --- a/config/date-scopes.php +++ b/config/date-scopes.php @@ -6,7 +6,7 @@ /** * If you want to include the current day/week/month/year etc. in the range, * you could use the inclusive range here as a default. - * Note that you can also optionally specify it for quite every scope we offer + * Note that you can also fluently specify it for quite every scope we offer * directly when using the scope: * Transaction::ofLast7Days(DateRange::INCLUSIVE); (this works for all but the singular "ofLast"-scopes) * This will do an inclusive query, even though the global default range here is set to exclusive. diff --git a/src/DateScopes.php b/src/DateScopes.php index 688e217..3964603 100755 --- a/src/DateScopes.php +++ b/src/DateScopes.php @@ -6,6 +6,23 @@ trait DateScopes { + /** + * For example, if you subtract one month from March 31, you would get February 31, which is not a valid date. + * The subMonthsNoOverflow method for instance would instead return February 28 (or February 29 in a leap year), + * as it adjusts the day to the last day of the month when the resulting date would be invalid. + * + * In Carbon, the date units that can have this "overflow" behavior are months, years, decades, etc. because their lengths can vary. + * Days, hours, minutes, and seconds have fixed lengths, so they do not have this issue. + * + * @var array|string[] + */ + private array $fixedLengthDateUnits = [ + 'second', + 'minute', + 'hour', + 'day' + ]; + /** * @param Builder $query Eloquent Builder * @param string $dateUnit A valid date unit, such as hour, day, month, year etc... @@ -21,23 +38,25 @@ public function scopeOfLastUnit(Builder $query, string $dateUnit, int $value, Da $startFunc = 'startOf'.ucfirst($dateUnit); $endFunc = 'endOf'.ucfirst($dateUnit); - $subFunc = 'sub'.ucfirst($dateUnit).'s'; + + $applyNoOverflow = (in_array($dateUnit, $this->fixedLengthDateUnits)) ? 'NoOverflow' : '' ; + $subFunc = 'sub'.ucfirst($dateUnit).'s'.$applyNoOverflow; $sub = ($dateUnit === 'second') ? 0 : 1; if ($dateRange === DateRange::EXCLUSIVE) { $range = [ - 'from' => now()->$startFunc()->$subFunc($value), - 'to' => now()->$endFunc()->$subFunc($sub), + 'from' => now()->$subFunc($value)->$startFunc(), + 'to' => now()->$subFunc($sub)->$endFunc(), ]; } else { $range = [ - 'from' => now()->$startFunc()->$subFunc($value - $sub), + 'from' => now()->$subFunc($value - $sub)->$startFunc(), 'to' => now()->$endFunc(), ]; } -// dd(collect($range)->transform(fn ($item) => $item->format('Y-m-d H:i:s'))); +// dd(collect($range)->transform(fn ($item) => $item->format('Y-m-d H:i:s'))->toArray()); return $query->whereBetween(config('date-scopes.created_column'), $range); }