Nova 指标允许您快速了解应用程序的关键业务指标。例如,您可以定义一个指标来显示每天添加到应用程序的用户总数,或某个产品的每周销售额。
Nova 提供了几种内置指标类型:数值、趋势、分区和进度。我们将检查每种指标类型并演示其用法。
数值指标显示单个值,如果需要,还会显示其与先前时间间隔的比较。例如,数值指标可能会显示过去三十天创建的用户总数与前三十天的比较。

数值指标可以使用 nova:value Artisan 命令生成。默认情况下,所有新指标都将放置在 app/Nova/Metrics 目录中。
php artisan nova:value NewUsers生成数值指标类后,您就可以对其进行自定义。每个数值指标类都包含一个 calculate 方法。此方法应返回一个 Laravel\Nova\Metrics\ValueResult 实例。不用担心,Nova 附带了各种帮助程序,可以快速生成指标结果。
在此示例中,我们使用 count 帮助程序,它将自动对指定的 Eloquent 模型执行 count 查询以获取选定范围,并自动检索“先前”范围的计数。
<?php
namespace App\Nova\Metrics;
use App\Models\User;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Metrics\Value;
class NewUsers extends Value
{
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
return $this->count($request, User::class);
}
/**
* Get the ranges available for the metric.
*
* @return array
*/
public function ranges()
{
return [
30 => '30 Days',
60 => '60 Days',
365 => '365 Days',
'TODAY' => 'Today',
'YESTERDAY' => 'Yesterday',
'MTD' => 'Month To Date',
'QTD' => 'Quarter To Date',
'YTD' => 'Year To Date',
];
}
/**
* Get the URI key for the metric.
*
* @return string
*/
public function uriKey()
{
return 'new-users';
}
}价值指标不仅仅提供 count 辅助函数。在构建指标时,您还可以使用各种其他聚合函数。现在让我们探索一下它们。
average 方法可用于计算给定列相对于先前时间间隔/范围的平均值。
return $this->average($request, Post::class, 'word_count');sum 方法可用于计算给定列相对于先前时间间隔/范围的总和。
return $this->sum($request, Order::class, 'price');max 方法可用于计算给定列相对于先前时间间隔/范围的最大值。
return $this->max($request, Order::class, 'total');min 方法可用于计算给定列相对于先前时间间隔/范围的最小值。
return $this->min($request, Order::class, 'total');每个值指标类都包含一个 ranges 方法。此方法确定值指标范围选择菜单中可用的范围。数组的键确定查询中应包含的天数,而值确定将放置在范围选择菜单中的“人类可读”文本。当然,您不需要定义任何范围。
/**
* Get the ranges available for the metric.
*
* @return array
*/
public function ranges()
{
return [
5 => '5 Days',
10 => '10 Days',
15 => '15 Days',
'TODAY' => 'Today',
'YESTERDAY' => 'Yesterday',
'MTD' => 'Month To Date',
'QTD' => 'Quarter To Date',
'YTD' => 'Year To Date',
'ALL' => 'All Time'
];
}TODAY / YESTERDAY / MTD / QTD / YTD / ALL 范围键
您可以根据需要自定义这些范围;但是,如果您使用的是内置的“今天”、“昨天”、“本月至今”、“本季度至今”、“本年至今”或“所有时间”范围,则不应更改其键。
默认情况下,Nova 会将 0 的结果处理为不包含数据的結果。这并不总是正确的,因此您可以使用 allowZeroResult 方法来指示 0 是有效的数值结果。
return $this->result(0)->allowZeroResult();您可以通过在返回 ValueResult 实例时调用 prefix 和 suffix 方法,在值指标的结果中添加前缀和/或后缀。
public function calculate(NovaRequest $request)
{
return $this->max($request, Order::class, 'total')
->prefix('$')
->suffix('per unit');
}您也可以使用 currency 方法指定给定值结果代表货币值。默认情况下,货币符号将为 $,但您也可以通过将符号作为参数传递给 currency 方法来指定您自己的货币符号。
return $this->max($request, Order::class, 'total')->currency();
return $this->max($request, Order::class, 'total')->currency('£');要自定义值结果的显示格式,您可以使用 format 方法。格式必须是 Numbro 支持的格式之一。
// Numbro v2.0+ (http://numbrojs.com/format.html)
public function calculate(NovaRequest $request)
{
return $this->count($request, User::class)
->format([
'thousandSeparated' => true,
'mantissa' => 2,
]);
}
// Numbro < v2.0 (http://numbrojs.com/old-format.html)
public function calculate(NovaRequest $request)
{
return $this->count($request, User::class)
->format('0,0');
}有时您可能需要在将值结果显示给用户之前对其进行“转换”。例如,假设您有一个“总收入”指标,该指标计算产品的总收入(以美分计)。您可能希望以美元而不是美分的方式向用户展示此值。要转换显示之前的值,可以使用 transform 助手。
public function calculate(NovaRequest $request)
{
return $this->sum($request, Invoice::class, 'amount')
->transform(fn($value) => $value / 100);
}如果您无法使用包含的查询助手来构建您的值指标,您可以轻松地使用 result 和 previous 方法手动向指标提供最终值,从而完全控制这些值的计算。
return $this->result(100)->previous(50);趋势指标通过折线图显示随时间推移的值。例如,趋势指标可能会显示过去三十天每天创建的新用户数量。

趋势指标可以使用 nova:trend Artisan 命令生成。默认情况下,所有新指标都将放置在 app/Nova/Metrics 目录中。
php artisan nova:trend UsersPerDay生成趋势指标类后,您就可以对其进行自定义。每个趋势指标类都包含一个 calculate 方法。此方法应返回一个 Laravel\Nova\Metrics\TrendResult 对象。不用担心,Nova 附带了各种助手,可以快速生成结果。
在此示例中,我们使用 countByDays 助手,它将自动对指定 Eloquent 模型执行 count 查询,以获取所选范围和所选间隔单位(在本例中为天)的结果。
<?php
namespace App\Nova\Metrics;
use App\Models\User;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Metrics\Trend;
class UsersPerDay extends Trend
{
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
return $this->countByDays($request, User::class);
}
/**
* Get the ranges available for the metric.
*
* @return array
*/
public function ranges()
{
return [
30 => '30 Days',
60 => '60 Days',
90 => '90 Days',
];
}
/**
* Get the URI key for the metric.
*
* @return string
*/
public function uriKey()
{
return 'users-per-day';
}
}趋势指标不仅提供 countByDays 助手。在构建指标时,您还可以使用各种其他聚合函数和时间间隔。
count 方法可用于计算给定列随时间的计数
return $this->countByMonths($request, User::class);
return $this->countByWeeks($request, User::class);
return $this->countByDays($request, User::class);
return $this->countByHours($request, User::class);
return $this->countByMinutes($request, User::class);average 方法可用于计算给定列随时间的平均值
return $this->averageByMonths($request, Post::class, 'word_count');
return $this->averageByWeeks($request, Post::class, 'word_count');
return $this->averageByDays($request, Post::class, 'word_count');
return $this->averageByHours($request, Post::class, 'word_count');
return $this->averageByMinutes($request, Post::class, 'word_count');sum 方法可用于计算给定列随时间的总计
return $this->sumByMonths($request, Order::class, 'price');
return $this->sumByWeeks($request, Order::class, 'price');
return $this->sumByDays($request, Order::class, 'price');
return $this->sumByHours($request, Order::class, 'price');
return $this->sumByMinutes($request, Order::class, 'price');max 方法可用于计算给定列随时间的最大值
return $this->maxByMonths($request, Order::class, 'total');
return $this->maxByWeeks($request, Order::class, 'total');
return $this->maxByDays($request, Order::class, 'total');
return $this->maxByHours($request, Order::class, 'total');
return $this->maxByMinutes($request, Order::class, 'total');min 方法可用于计算给定列随时间的最小值
return $this->minByMonths($request, Order::class, 'total');
return $this->minByWeeks($request, Order::class, 'total');
return $this->minByDays($request, Order::class, 'total');
return $this->minByHours($request, Order::class, 'total');
return $this->minByMinutes($request, Order::class, 'total');每个趋势指标类都包含一个 ranges 方法。此方法确定趋势指标范围选择菜单中可用的范围。数组的键确定查询中应包含的时间间隔单位(月、周、天等)的数量,而值确定将放置在范围选择菜单中的“人类可读”文本。当然,您不需要定义任何范围
/**
* Get the ranges available for the metric.
*
* @return array
*/
public function ranges()
{
return [
5 => '5 Days',
10 => '10 Days',
15 => '15 Days',
];
}有时,您可能希望强调最新趋势指标时间间隔的值。例如,在此屏幕截图中,在过去一天内创建了六个用户

要实现此目的,您可以使用 showLatestValue 方法
return $this->countByDays($request, User::class)
->showLatestValue();要自定义值结果的显示格式,您可以使用 format 方法。格式必须是 Numbro 支持的格式之一。
// Numbro v2.0+ (http://numbrojs.com/format.html)
public function calculate(NovaRequest $request)
{
return $this->count($request, User::class)
->format([
'thousandSeparated' => true,
'mantissa' => 2,
]);
}
// Numbro < v2.0 (http://numbrojs.com/old-format.html)
public function calculate(NovaRequest $request)
{
return $this->count($request, User::class)
->format('0,0');
}默认情况下,Nova 仅将趋势指标的最后一个值显示为强调的“当前”值。但是,有时您可能希望显示趋势的总计数。您可以通过在从趋势指标返回值时调用 showSumValue 方法来实现此目的
return $this->countByDays($request, User::class)
->showSumValue();有时您可能希望在强调的“当前”趋势值中添加前缀或后缀。要实现此目的,您可以使用 prefix 和 suffix 方法
return $this->sumByDays($request, Order::class, 'price')->prefix('$');如果您的趋势指标显示货币价值,您可以使用 dollars 和 euros 方便方法,以快速将美元或欧元符号添加为趋势值的的前缀
return $this->sumByDays($request, Order::class, 'price')->dollars();如果您无法使用提供的查询助手来构建趋势指标,您可以手动构建 Laravel\Nova\Metrics\TrendResult 对象并从指标的 calculate 方法中返回它。这种计算趋势数据的方法在构建要绘制的数据时为您提供了完全的灵活性。
return (new TrendResult)->trend([
'July 1' => 100,
'July 2' => 150,
'July 3' => 200,
]);分区指标显示一个饼图,其中包含各个值的比例。例如,分区指标可以显示应用程序提供的每个计费计划的用户总数。

可以使用 nova:partition Artisan 命令生成分区指标。默认情况下,所有新的指标都将放置在 app/Nova/Metrics 目录中。
php artisan nova:partition UsersPerPlan生成分区指标类后,您就可以对其进行自定义。每个分区指标类都包含一个 calculate 方法。此方法应返回一个 Laravel\Nova\Metrics\PartitionResult 对象。不用担心,Nova 附带了各种助手,可以快速生成结果。
在本例中,我们使用的是 count 助手,它将自动对指定的 Eloquent 模型执行 count 查询,并检索属于指定“分组依据”列的每个不同值的模型数量。
<?php
namespace App\Nova\Metrics;
use App\Models\User;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Metrics\Partition;
class UsersPerPlan extends Partition
{
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
return $this->count($request, User::class, 'stripe_plan');
}
/**
* Get the URI key for the metric.
*
* @return string
*/
public function uriKey()
{
return 'users-by-plan';
}
}分区指标不仅提供 count 助手。在构建指标时,您还可以使用各种其他聚合函数。
average 方法可用于计算给定列在不同组内的平均值。例如,以下对 average 方法的调用将显示一个饼图,其中包含公司每个部门的平均订单价格。
return $this->average($request, Order::class, 'price', 'department');sum 方法可用于计算给定列在不同组内的总和。例如,以下对 sum 方法的调用将显示一个饼图,其中包含公司每个部门的所有订单价格的总和。
return $this->sum($request, Order::class, 'price', 'department');max 方法可用于计算给定列在不同组内的最大值。例如,以下对 max 方法的调用将显示一个饼图,其中包含公司每个部门的最高订单价格。
return $this->max($request, Order::class, 'price', 'department');min 方法可用于计算给定列在不同组内的最小值。例如,以下对 min 方法的调用将显示一个饼图,其中包含公司每个部门的最低订单价格。
return $this->min($request, Order::class, 'price', 'department');通常,将分区指标划分为组的列值将是简单的键,而不是“人类可读”的内容。或者,如果您显示按布尔值列分组的分区指标,Nova 将显示您的组标签为“0”和“1”。为此,Nova 允许您提供一个闭包,将标签格式化为更易读的内容
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
return $this->count($request, User::class, 'stripe_plan')
->label(fn ($value) => match ($value) {
null => 'None',
default => ucfirst($value)
});
}默认情况下,Nova 将选择分区指标中使用的颜色。有时,您可能希望更改这些颜色以更好地匹配它们所代表的数据类型。为此,您可以在从指标返回分区结果时调用 colors 方法
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
// This metric has `audio`, `video`, and `photo` types...
return $this->count($request, Post::class, 'type')->colors([
'audio' => '#6ab04c',
'video' => 'rgb(72,52,212)',
// Since it is unspecified, "photo" will use a default color from Nova...
]);
}如果您无法使用包含的查询助手来构建分区指标,您可以使用 result 方法手动将最终值提供给指标,从而提供最大的灵活性
return $this->result([
'Group 1' => 100,
'Group 2' => 200,
'Group 3' => 300,
]);进度指标在条形图中显示当前进度与目标值的对比。例如,进度指标可能会显示本月注册的用户数量与目标目标的对比

进度指标可以使用 nova:progress Artisan 命令生成。默认情况下,所有新的指标都将放置在 app/Nova/Metrics 目录中
php artisan nova:progress NewUsers生成进度指标类后,您就可以对其进行自定义。每个进度指标类都包含一个 calculate 方法。此方法应返回一个 Laravel\Nova\Metrics\ProgressResult 对象。不用担心,Nova 附带了各种助手,可以快速生成结果。
在此示例中,我们使用 count 助手来确定我们是否已达到本月的用户注册目标。count 助手将自动对指定的 Eloquent 模型执行 count 查询
<?php
namespace App\Nova\Metrics;
use App\Models\User;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Metrics\Progress;
class NewUsers extends Progress
{
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
return $this->count($request, User::class, function ($query) {
return $query->where('created_at', '>=', now()->startOfMonth());
}, target: 200);
}
/**
* Get the URI key for the metric.
*
* @return string
*/
public function uriKey()
{
return 'new-users';
}
}进度指标不仅附带 count 助手。您也可以在构建指标时使用 sum 聚合方法。例如,以下对 sum 方法的调用将显示一个进度指标,其中包含已完成交易金额的总计与目标销售目标的对比
return $this->sum($request, Transaction::class, function ($query) {
return $query->where('completed', '=', 1);
}, 'amount', target: 2000);有时你可能正在跟踪朝着你想要避免的“目标”的进度,例如在一个给定月份内取消的客户数量。在这种情况下,你通常希望进度指标的颜色不再是绿色,因为你正在接近你的“目标”。
当使用 avoid 方法指定指标是你想要避免的时,Nova 将使用绿色来表示没有朝着“目标”前进,而使用黄色来表示接近“目标”的完成。
return $this->count($request, User::class, function ($query) {
return $query->where('cancelled_at', '>=', now()->startOfMonth());
}, target: 200)->avoid();有时你可能希望在当前进度值之前或之后添加前缀或后缀。为了实现这一点,你可以使用 prefix 和 suffix 方法。
return $this->sum($request, Transaction::class, function ($query) {
return $query->where('completed', '=', 1);
}, 'amount', target: 2000)->prefix('$');如果你的进度指标显示的是货币价值,你可以使用 dollars 和 euros 方便方法来快速在进度值之前添加美元或欧元符号。
return $this->sum($request, Transaction::class, function ($query) {
return $query->where('completed', '=', 1);
}, 'amount', target: 2000)->dollars();如果你无法使用包含的查询助手来构建你的进度指标,你可以使用 result 方法手动将最终值提供给指标。
return $this->result(80, 100);表格指标允许你显示自定义的链接列表以及操作列表,以及可选的图标。
表格指标可以使用 nova:table Artisan 命令生成。默认情况下,所有新的指标都将放置在 app/Nova/Metrics 目录中。
php artisan nova:table NewReleases一旦你的表格指标类生成,你就可以开始自定义它。每个表格指标类都包含一个 calculate 方法。此方法应该返回一个 Laravel\Nova\Metrics\MetricTableRow 对象数组。每个指标行允许你指定一个标题和副标题,它们将堆叠在行上显示。
<?php
namespace App\Nova\Metrics;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Metrics\Table;
class NewReleases extends Table
{
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
return [
MetricTableRow::make()
->title('v1.0')
->subtitle('Initial release of Laravel Nova'),
MetricTableRow::make()
->title('v2.0')
->subtitle('The second major series of Laravel Nova'),
];
}
}虽然表格指标非常适合显示进度、文档链接或模型的最新条目,但通过将操作附加到它们,它们会变得更加强大。

您可以使用 actions 方法返回一个 Laravel\Nova\Menu\MenuItem 实例数组,这些实例将显示在下拉菜单中
<?php
namespace App\Nova\Metrics;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Metrics\Table;
class NewReleases extends Table
{
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
return [
MetricTableRow::make()
->title('v1.0')
->subtitle('Initial release of Laravel Nova')
->actions(function () {
return [
MenuItem::externalLink('View release notes', '/releases/1.0'),
MenuItem::externalLink('Share on Twitter', 'https://twitter.com/intent/tweet?text=Check%20out%20the%20new%20release'),
];
}),
MetricTableRow::make()
->title('v2.0 (pre-release)')
->subtitle('The second major series of Laravel Nova')
->actions(function () {
return [
MenuItem::externalLink('View release notes', '/releases/2.0'),
MenuItem::externalLink('Share on Twitter', 'https://twitter.com/intent/tweet?text=Check%20out%20the%20new%20release'),
];
}),
];
}
}自定义菜单项
您可以通过阅读 菜单项自定义文档 来了解更多关于菜单自定义的信息。
表格指标还支持在每行的标题和副标题左侧显示一个图标。您可以使用此信息通过类型或使用它们来显示内部进程的进度来直观地区分不同的表格行。
![]()
要在表格指标行上显示图标,请使用 icon 方法并传入要使用的图标的键
<?php
namespace App\Nova\Metrics;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Metrics\Table;
class NextSteps extends Table
{
/**
* Calculate the value of the metric.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return mixed
*/
public function calculate(NovaRequest $request)
{
return [
MetricTableRow::make()
->icon('check-circle')
->iconClass('text-green-500')
->title('Get your welcome kit from HR')
->subtitle('Includes a Macbook Pro and swag!'),
MetricTableRow::make()
->icon('check-circle')
->iconClass('text-green-500')
->title('Bootstrap your development environment')
->subtitle('Install the repository and get your credentials.'),
MetricTableRow::make()
->icon('check-circle')
->iconClass('text-gray-400 dark:text-gray-700')
->title('Make your first production deployment')
->subtitle('Push your first code change to our servers.'),
];
}
}您可以通过使用 iconClass 方法向图标添加必要的类来通过 CSS 自定义图标的颜色
MetricTableRow::make()
->icon('check-circle')
->iconClass('text-gray-400 dark:text-gray-700')
->title('Make your first production deployment')
->subtitle('Push your first code change to our servers.'),Heroicons
Nova 使用来自设计师 Steve Schoger 的免费图标集 Heroicons UI。您可以随意使用这些图标来匹配 Nova 内置图标的外观和感觉。
如果您动态地为表格指标生成行,则可能存在没有结果可显示的情况。默认情况下,Nova 会向用户显示“未找到结果...”。
但是,有时您可能希望自定义此文本以向用户提供更多上下文。例如,名为“最近用户”的指标可能没有用户可显示,因为没有最近用户。在这些情况下,您可以使用 emptyText 方法自定义“无结果”消息
use App\Nova\Metrics\RecentUsers;
use Laravel\Nova\Http\Requests\NovaRequest;
/**
* Get the cards available for the resource.
*
* @param \Laravel\Nova\Http\Requests\NovaRequest $request
* @return array
*/
public function cards(NovaRequest $request)
{
return [
RecentUsers::make()->emptyText('There are no recent users.');
];
}有时,计算指标的值可能很慢且成本高昂。为此,所有 Nova 指标都包含一个 cacheFor 方法,允许您指定指标结果的缓存持续时间。
/**
* Determine the amount of time the results of the metric should be cached.
*
* @return \DateTimeInterface|\DateInterval|float|int|null
*/
public function cacheFor()
{
return now()->addMinutes(5);
}默认情况下,Nova 将使用指标类名作为指标的可显示名称。您可以通过覆盖指标类中的 name 方法来自定义指标卡上显示的指标名称。
/**
* Get the displayable name of the metric
*
* @return string
*/
public function name()
{
return 'Users Created';
}