---
title: "Trends — Metrics for Laravel | Roundly"
description: "Time series bucketed by minute to year, with gap filling, multi-series groupBy() and date grouping portable across four databases."
url: https://roundly-consulting.com/open-source/docs/metrics-for-laravel/trends
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Metrics for Laravel](https://roundly-consulting.com/open-source/docs/metrics-for-laravel.md)

# Trends

A trend aggregates a value over time, bucketed by a unit, and returns a trends map keyed by bucket:

```php
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:

```php
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:

```php
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 totals stay under trends — the ungrouped trend over the same range, i.e. the metric’s own aggregate over every row (an average of all rows, not a sum of the series’ averages) — 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:

```php
Metrics::trend()->count(User::query(), 'created_at')->daily()->groupBy('plan')->toArray();
// result => [
//   'trends' => ['2024-01-01' => 30.0, ...],            // the ungrouped trend
//   'series' => [
//     'pro'  => ['2024-01-01' => 20.0, ...],
//     'free' => ['2024-01-01' => 10.0, ...],
//   ],
// ]
```

A NULL group and an empty-string group share the series key '', so their buckets are merged with the metric’s aggregate — summed for count and sum, the larger or smaller for max and min, a weighted average for average — rather than one overwriting the other. A boolean dimension is keyed 0 / 1 on every database.

The query’s own orderBy() is ignored: buckets always come back in date order, and an ordered query (latest()) does not break the GROUP BY on PostgreSQL or MySQL. The date column is quoted by the query grammar, so a camelCase or reserved-word name (occurredAt, order) works.

## 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 crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## 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.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/metrics-for-laravel.md)
