A trend aggregates a value over time, bucketed by a unit, and returns a trends map keyed by bucket:
use App\Models\User;
use RoundlyConsulting\Metrics\Enums\Period;
use RoundlyConsulting\Metrics\Facades\Metrics;
Metrics::trend()
->count(User::query(), 'created_at')
->daily()
->range(Period::MonthToDate)
->toArray();
// result => ['trends' => ['2024-06-01' => 12.0, '2024-06-02' => 0.0, ...]]Units
Set the unit with unit() — a Unit case or its string — or a convenience helper. The default is DAY unless you change metrics.default_unit; an unknown unit string is ignored and the current unit is kept:
use RoundlyConsulting\Metrics\Enums\Unit;
$metric->unit(Unit::Hour); // or ->unit('HOUR')
$metric->perMinute();
$metric->hourly();
$metric->daily(); // the default (config: metrics.default_unit)
$metric->weekly();
$metric->monthly();
$metric->yearly();| Value | Enum case | Helper | Bucket key |
|---|---|---|---|
MINUTE | Unit::Minute | perMinute() | Y-m-d H:i:00 |
HOUR | Unit::Hour | hourly() | Y-m-d H:00 |
DAY | Unit::Day | daily() | Y-m-d |
WEEK | Unit::Week | weekly() | o-W — ISO year and week, e.g. 2022-52 |
MONTH | Unit::Month | monthly() | Y-m |
YEAR | Unit::Year | yearly() | Y |
Gap filling
Every bucket in the selected range is present in the output — empty buckets are filled with 0 so charts get a continuous axis. The axis starts at the bucket that holds the range start (the ISO week of January 1st for a weekly YTD, the month of the start for a monthly 90), so the bucket holding “now” is always there. Opt out to return only the buckets that have rows:
Metrics::trend()->count(User::query(), 'created_at')->daily()->withoutGapFilling()->toArray();
$metric->withoutGapFilling(); // only buckets that have rows
$metric->withoutGapFilling(false); // re-enable filling (the default)With the ALL range, the span is inferred from the first and last buckets that have data.
Multi-series trends
Split a trend by a dimension with groupBy(). The combined totals stay under trends, and an additive series key holds one bucket set per dimension value — each gap-filled across the same range, so every series shares the same buckets:
Metrics::trend()->count(User::query(), 'created_at')->daily()->groupBy('plan')->toArray();
// result => [
// 'trends' => ['2024-01-01' => 30.0, ...], // totals across series
// 'series' => [
// 'pro' => ['2024-01-01' => 20.0, ...],
// 'free' => ['2024-01-01' => 10.0, ...],
// ],
// ]Database support
Bucketing uses a database-specific SQL date expression. MySQL, MariaDB, PostgreSQL and SQLite are supported out of the box, so the same trend runs unchanged in development and production. Week buckets are ISO-8601 on every driver — including SQLite, which has no native ISO week. Any other driver throws MissingTrendQueryExpressionException until you map an expression for it in config('metrics.trend_drivers') (see Extending).
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 cryptoBy 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.