NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Metrics for Laravel

Period comparison

Value and Progress metrics can compute period-over-period change. withChangeAgainstPreviousPeriod() compares against the immediately-preceding window:

use App\Models\User;
use RoundlyConsulting\Metrics\Enums\Period;
use RoundlyConsulting\Metrics\Facades\Metrics;

Metrics::value()
    ->count(User::query())
    ->range(Period::Today)
    ->withChangeAgainstPreviousPeriod()   // vs. yesterday
    ->toArray();
// result => ['value' => 42.0, 'previous' => 30.0, 'change' => ['percentage' => 40.0, 'is_increase' => true]]

The previous window

RangeCompared against
7 … 365The N days before the current window
TODAY, YESTERDAYThe day before
WTD, MTD, QTD, YTDThe same elapsed span of the previous week, month, quarter or year
THIS_WEEK … THIS_YEARThe whole previous week, month, quarter or year
LAST_WEEK … LAST_YEARThe period before that
CUSTOMA window of exactly the same length that ends one second before it starts — whole days when the end is a bare date
ALLNo comparison

The ALL range has no bounded window, so only the value is returned.

Compare against any range

compareTo() compares against any period instead — and implies withChangeAgainstPreviousPeriod(). The comparison period resolves as-is, in the metric’s timezone:

use RoundlyConsulting\Metrics\Enums\Period;

Metrics::value()
    ->count(Order::query())
    ->range(Period::ThisMonth)
    ->compareTo(Period::LastYear)   // vs. the whole previous calendar year
    ->toArray();

// The same month a year ago — an explicit custom window
Metrics::value()
    ->count(Order::query())
    ->range(Period::ThisMonth)
    ->compareTo(Period::Custom, '2025-09-01 00:00:00', '2025-09-30 23:59:59')
    ->toArray();

Progress change

Progress metrics add the previous value’s progress and a change block with the percentage change and the deltas in value and in progress points — a ProgressResult is also a ValueResult, so change() returns the percentage:

Metrics::progress()
    ->sum(Order::query(), 'total')
    ->target(10000)
    ->range(Period::MonthToDate)
    ->withChangeAgainstPreviousPeriod()
    ->toArray();
// result => [
//     'value' => 8200.0, 'progress' => 82.0,
//     'previous' => 6500.0, 'previous_progress' => 65.0,
//     'target' => 10000.0, 'avoid' => false,
//     'change' => ['percentage' => 26.0, 'is_increase' => true, 'progress' => 17.0, 'value' => 1700.0],
// ]

The change percentage is measured against the size of the previous value, so its sign always follows the direction of the change: from -100 to -50 is +50. From a previous value of zero it is 100 for a rise, -100 for a fall and 0 when the value stays at zero. change.is_increase is true when the current value exceeds the previous one. Without a comparison, previous, change.percentage and change.is_increase are null — on a progress metric every change field is.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.