---
title: "Caching — Metrics for Laravel | Roundly"
description: "Opt-in result caching — globally or per metric with cache(), cacheFor(), cacheKey() and dontCache() — plus forget() and flushCache()."
url: https://roundly-consulting.com/open-source/docs/metrics-for-laravel/caching
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)

# Caching

Caching is off by default. When enabled, a metric’s computed result is remembered for a TTL in a cache store, so busy dashboards don’t recompute the same aggregates on every request.

## Enable globally

```php
// config/metrics.php
'cache' => [
    'enabled' => env('METRICS_CACHE_ENABLED', false),  // master switch
    'store' => env('METRICS_CACHE_STORE'),             // null = default store
    'ttl' => env('METRICS_CACHE_TTL', 300),            // seconds
    'prefix' => 'metrics',                             // cache-key prefix
],
```

Set METRICS\_CACHE\_ENABLED=true to cache every metric.

## Per-metric control

Each metric can opt in or out at runtime, regardless of the config switch:

| Method | Effect |
| --- | --- |
| `cache(?int $ttl = null)` | Enable caching; optionally override the TTL in seconds. |
| `cacheFor(DateTimeInterface\|int $ttl)` | Enable caching until a moment, or for N seconds. A moment is resolved when each entry is written, so a registered template stops caching once it has passed. |
| `cacheKey(string $key)` | Use an explicit cache key instead of the generated one. |
| `dontCache()` | Disable caching for this metric even when config enables it. |

```php
use RoundlyConsulting\Metrics\Facades\Metrics;

Metrics::value()->count(User::query())->range('TODAY')->cacheFor(now()->addHour())->toArray();

Metrics::value()->count(User::query())->cache()->toArray();                         // config TTL
Metrics::value()->count(User::query())->cache(600)->toArray();                      // 10 minutes
Metrics::value()->count(User::query())->cacheFor(now()->endOfDay())->toArray();     // until midnight
Metrics::value()->count(User::query())->cache()->cacheKey('users:all')->toArray();  // explicit key
Metrics::value()->count(User::query())->dontCache()->toArray();                     // always fresh
```

## Cache keys

Without an explicit cacheKey(), the key is {prefix}:{md5} over the metric class, its registry key, the range and its custom start/end, the reporting and app timezones, the precision and rounding mode, an inline builder’s query and aggregate or a metric class’s own state (below), and type-specific discriminators — a trend’s unit, gap filling and series column; a value metric’s comparison settings, plus the target for progress metrics; a partition’s limit and translated “Other” label. Differently configured calls to the same metric — and inline builders versus registered metrics of the same class — cache separately and never collide.

A metric class’s own state is every property its class declares, constructor arguments included: a model counts as its class and key, an Eloquent or query builder as its SQL and bindings, an enum as its value, a date as its instant, and any other object (an injected service) as its class. So new TenantRevenue($tenant) caches one entry per tenant, and Users::make('count') and Users::make('sum', 'balance') never share one. A metric holding a closure has no stable identity and is calculated without the cache.

State the metric reads while calculating rather than holding in a property — auth()->user(), the request — is invisible to the key. Override cacheIdentity() to add it:

```php
protected function cacheIdentity(): ?array
{
    return [...parent::cacheIdentity() ?? [], 'team' => auth()->user()?->current_team_id];
}
```

A custom cacheKey() is used verbatim — the prefix is not applied and range changes don’t alter it, so keep it unique yourself. Only the result portion is cached, as plain data — result() rebuilds the typed object from it, and the envelope around it is rebuilt on every call. Presentation is applied on every read and never cached — formatUsing() and a partition’s labelUsing() labels.

## Forgetting cached results

```php
Metrics::forget('revenue');       // every cached range/timezone/option of one registered metric
Metrics::forget($metric);         // an unregistered metric instance (by its class)
Metrics::flushCache();            // every cached metric result
```

Invalidation works on every cache store, with or without tags: each entry records the cache generation it was written under, and forgetting starts a new generation, so older entries are recalculated on their next read and overwritten in place. It covers custom cacheKey() entries too. forget() throws UnknownMetricException for a key that isn’t registered.

## TTL from .env

METRICS\_CACHE\_ENABLED accepts true, false, 1, 0, on, off, yes and no; METRICS\_CACHE\_TTL is a whole number of seconds between 1 and 31536000 (one year) — the string .env produces is honoured. An unusable TTL or switch value (say METRICS\_CACHE\_ENABLED=disabled) throws InvalidConfigurationException instead of silently falling back to the default; a blank one (METRICS\_CACHE\_TTL=) is not set and takes it.

The MetricCalculated event’s fromCache flag tells you whether a resolution was a cache hit — see Events.

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