Skip to content
laravel-nova-examples.com

Show revenue as a value metric with ranges and caching

A Nova 5 value metric that sums order totals, compares them with the previous period, formats them as currency and caches the result.

Nova
5.x
Published
Reading time
2 min

A revenue card at the top of the orders resource answers the first question most admins have: how much did we sell in the selected period, and is that more or less than before? Nova's Value metric does the period comparison for you.

Generate the metric

php artisan nova:value Revenue

The metric class

<?php

namespace App\Nova\Metrics;

use App\Models\Order;
use DateTimeInterface;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Metrics\Value;
use Laravel\Nova\Metrics\ValueResult;
use Laravel\Nova\Nova;

class Revenue extends Value
{
    public function calculate(NovaRequest $request): ValueResult
    {
        return $this->sum(
            $request,
            Order::where('status', '!=', 'cancelled'),
            'total',
        )->currency('€');
    }

    public function ranges(): array
    {
        return [
            30 => Nova::__('30 Days'),
            60 => Nova::__('60 Days'),
            365 => Nova::__('365 Days'),
            'MTD' => Nova::__('Month To Date'),
            'QTD' => Nova::__('Quarter To Date'),
            'YTD' => Nova::__('Year To Date'),
        ];
    }

    public function cacheFor(): DateTimeInterface|null
    {
        return now()->addMinutes(5);
    }
}

What each part does

  • sum() accepts either a model class or a query builder. Passing Order::where(...) excludes cancelled orders without a separate scope.
  • Nova calculates the value for the selected range and for the previous period of the same length, then shows the percentage change.
  • currency('€') prefixes the number with a currency symbol. Without an argument Nova uses $.
  • cacheFor() caches the result per range. Revenue rarely needs to be real-time, and the sum over a large orders table is one of the more expensive queries on an admin dashboard.

The numeric keys in ranges() are days. The string keys MTD, QTD and YTD are recognized by Nova and compared with the same span of the previous month, quarter or year.

Register the card on the resource

use Laravel\Nova\Http\Requests\NovaRequest;

public function cards(NovaRequest $request): array
{
    return [
        Metrics\Revenue::make(),
    ];
}

To show it on the dashboard instead, add it to the cards() method of a dashboard class such as App\Nova\Dashboards\Main.

Index on the date column

Value metrics filter on created_at by default. If the orders table is large, make sure the column the metric filters on is indexed, otherwise every range switch triggers a full table scan.

Reference