Filament Tables 汇总行(Summaries)完全指南:在 Laravel 表格中实现平均值、计数、范围与求和统计
【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps & admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament
Filament 的 Tables 包允许你在表格内容下方渲染一个"汇总(summary)"区域,用于展示当前数据集的计算结果,例如平均值、总和、计数和范围。本指南以 packages/tables/docs/06-summaries.md 为核心,结合 packages/tables/src/Columns/Summarizers 目录下的源码实现,系统讲解内置四类汇总器(Average / Count / Range / Sum)的用法、格式化能力、作用域控制、自定义汇总器,以及配合分组(grouping)实现报表级汇总的实战方案。读完本文,你将能够为任意表格列快速接入统计汇总,并精确控制其展示形式与计算范围。
汇总(Summaries)是什么
默认情况下,Filament 表格会在数据下方渲染一个汇总区域,包含:
- 当前页数据(this page)的汇总行;
- 当存在多页数据时,额外渲染全部数据(all data)的总计汇总行。
此外,如果表格启用了分组(grouping),你还可以为每个分组单独渲染汇总,详见下文"汇总分组行"一节。
汇总器(Summarizer)对象可以通过任意表格列的summarize()方法挂载。该方法定义在 packages/tables/src/Columns/Concerns/CanBeSummarized.php,它接收单个汇总器或汇总器数组,并将每个汇总器与当前列进行绑定($summarizer->column($this))。单个列可以同时挂载多个汇总器:
use Filament\Tables\Columns\Summarizers\Average; use Filament\Tables\Columns\Summarizers\Range; use Filament\Tables\Columns\TextColumn; TextColumn::make('rating') ->numeric() ->summarize([ Average::make(), Range::make(), ])注意:表格的第一列不能使用汇总器,因为该列被专门用于渲染汇总区域的标题(heading)与副标题(subheading)。
内置汇总器一览
Filament 默认提供四类汇总器,均继承自抽象基类 Summarizer,类文件位于packages/tables/src/Columns/Summarizers/目录:
| 汇总器 | 类名 | 说明 | 底层查询 |
|---|---|---|---|
| 平均值 Average | Average | 计算数据集中所有值的平均值 | avg($attribute) |
| 计数 Count | Count | 统计数据集中的值(行)总数 | count($attribute) |
| 范围 Range | Range | 计算数据集中的最小值与最大值 | min()/max() |
| 求和 Sum | Sum | 计算数据集中所有值的总和 | sum($attribute) |
在 Average.php、Sum.php、Count.php、Range.php 的summarize()方法中可以看到,每个汇总器最终都是基于Illuminate\Database\Query\Builder执行对应的 SQL 聚合函数,因此统计完全在数据库端完成,不会把全量数据加载到内存。
你也可以创建自定义汇总器,以任意方式展示数据。
平均值(Average)
使用Average计算数据集中所有值的平均值——例如将所有评分相加后除以评分数量:
use Filament\Tables\Columns\Summarizers\Average; use Filament\Tables\Columns\TextColumn; TextColumn::make('rating') ->summarize(Average::make())从源码可以看出,Average在setUp()中会自动调用$this->numeric()(见 Average.php),即平均值默认按数字格式展示。
计数(Count)
使用Count统计数据集中的值(行)总数。如果只是统计行数,直接使用即可;若需要统计满足特定条件的记录数,通常需要配合作用域(scoping)使用:
use Filament\Tables\Columns\IconColumn; use Filament\Tables\Columns\Summarizers\Count; use Illuminate\Database\Query\Builder; IconColumn::make('is_published') ->boolean() ->summarize( Count::make()->query(fn (Builder $query) => $query->where('is_published', true)), ),上述示例会计算"已发布文章"的数量。
统计图标的出现次数
当 Count 作用于图标列(IconColumn)时,可以使用icons()方法,以图形化方式向用户展示表格中每种图标分别出现了多少次:
use Filament\Tables\Columns\IconColumn; use Filament\Tables\Columns\Summarizers\Count; use Illuminate\Database\Query\Builder; IconColumn::make('is_published') ->boolean() ->summarize(Count::make()->icons()),从源码 Count.php 可以看到,icons()模式下的计数逻辑是:对列中出现的每个去重取值,结合图标列的getIcon()/getColor()解析出对应的图标与颜色,然后统计每个图标组合出现的次数,最终渲染为一个「次数 + 图标」的列表(渲染逻辑见toEmbeddedHtml(),Count.php)。需要注意的是,若对非IconColumn列调用icons(),会抛出LogicException(Count.php)。
范围(Range)
使用Range计算数据集中的最小值和最大值:
use Filament\Tables\Columns\Summarizers\Range; use Filament\Tables\Columns\TextColumn; TextColumn::make('price') ->summarize(Range::make())上述示例会找出表格中的最低价和最高价。从源码看,Range.php 的summarize()会执行一条selectRaw("min(...) as ..., max(...) as ...")的查询并返回[最小值, 最大值]二元组。
日期范围
使用minimalDateTimeDifference()可以将范围格式化为日期,并以"最小差异"方式展示:
use Filament\Tables\Columns\Summarizers\Range; use Filament\Tables\Columns\TextColumn; TextColumn::make('created_at') ->dateTime() ->summarize(Range::make()->minimalDateTimeDifference())该方法会呈现最小值与最大值之间的最小差异(实现见 Range.php),具体规则:
- 如果最小、最大日期不是同一天,只显示日期;
- 如果最小、最大日期在同一天但时间不同,日期和时间都显示;
- 如果最小、最大日期时间完全相同,只显示一次。
文本范围
使用minimalTextualDifference()将范围格式化为文本:
use Filament\Tables\Columns\Summarizers\Range; use Filament\Tables\Columns\TextColumn; TextColumn::make('sku') ->summarize(Range::make()->minimalTextualDifference())该方法呈现两个值之间的最小差异(实现见 Range.php):
- 如果最小、最大值首字母不同,只显示第一个字母;
- 如果首字母相同,则持续追加字符直到出现差异为止(大小写不敏感比较);
- 如果两个值完全相同,只显示一次。
范围中是否包含 null 值
默认情况下,Range会从计算中排除 null 值(源码中$shouldExcludeNull默认为true,并在查询中自动追加whereNotNull($attribute),见 Range.php)。如需包含 null 值,可以使用excludeNull(false):
use Filament\Tables\Columns\Summarizers\Range; use Filament\Tables\Columns\TextColumn; TextColumn::make('sku') ->summarize(Range::make()->excludeNull(false))求和(Sum)
使用Sum计算数据集中所有值的总和:
use Filament\Tables\Columns\Summarizers\Sum; use Filament\Tables\Columns\TextColumn; TextColumn::make('price') ->summarize(Sum::make())上述示例会将表中所有价格相加。与Average相同,Sum也会在setUp()中默认启用数字格式化(见 Sum.php)。
设置标签
使用label()方法为汇总器设置标签:
use Filament\Tables\Columns\Summarizers\Sum; use Filament\Tables\Columns\TextColumn; TextColumn::make('price') ->summarize(Sum::make()->label('Total'))不设置时,会回退到各汇总器getDefaultLabel()返回的翻译字符串(如filament-tables::table.summary.summarizers.sum.label,见 Sum.php)。label()也可以传入闭包进行动态求值,并支持translateLabel()开启翻译(见 HasLabel.php)。
隐藏标签
不建议通过将标签设置为空字符串来"隐藏"它:虽然视觉上达到了隐藏效果,但屏幕阅读器(screen readers)将无法感知该汇总器的用途。正确做法是使用hiddenLabel(),它在视觉上隐藏标签的同时,仍将其保留在无障碍树中(渲染时给标签追加fi-sr-onlyclass,见 Summarizer.php):
use Filament\Tables\Columns\Summarizers\Sum; use Filament\Tables\Columns\TextColumn; TextColumn::make('price') ->summarize(Sum::make()->hiddenLabel())hiddenLabel()也支持传入布尔值或闭包进行条件隐藏:
use Filament\Tables\Columns\Summarizers\Sum; use Filament\Tables\Columns\TextColumn; TextColumn::make('price') ->summarize(Sum::make()->hiddenLabel(FeatureFlag::active()))作用域(Scoping the dataset)
使用query()方法可以为汇总器的数据集施加数据库查询作用域。query()既接受闭包(作为查询修改器,保存在modifyQueryUsing),也接受查询构建器实例(见 InteractsWithTableQuery.php):
use Filament\Tables\Columns\Summarizers\Average; use Filament\Tables\Columns\TextColumn; use Illuminate\Database\Query\Builder; TextColumn::make('rating') ->summarize( Average::make()->query(fn (Builder $query) => $query->where('is_published', true)), ),上述示例中,只有is_published为true的行才会参与平均值计算。
该特性与计数汇总器配合尤为实用——可以统计数据集中通过某项测试的记录数:
use Filament\Tables\Columns\IconColumn; use Filament\Tables\Columns\Summarizers\Count; use Illuminate\Database\Query\Builder; IconColumn::make('is_published') ->boolean() ->summarize( Count::make()->query(fn (Builder $query) => $query->where('is_published', true)), ),在底层,Summarizer::getState()会对传入的查询clone()一份再进行计算(见 Summarizer.php),因此作用域修改不会污染表格主查询;同时源码中还处理了关联关系列(通过whereHas反向关联统计)与多对多中间表列(pivot.xxx)等复杂场景。
格式化(Formatting)
数字格式化
numeric()方法将汇总值格式化为数字:
use Filament\Tables\Columns\Summarizers\Average; use Filament\Tables\Columns\TextColumn; TextColumn::make('rating') ->summarize(Average::make()->numeric())可以通过decimalPlaces参数自定义小数位数:
use Filament\Tables\Columns\Summarizers\Average; use Filament\Tables\Columns\TextColumn; TextColumn::make('rating') ->summarize(Average::make()->numeric( decimalPlaces: 0, ))默认使用应用(app)的 locale 进行格式化;也可以传入locale参数指定:
use Filament\Tables\Columns\Summarizers\Average; use Filament\Tables\Columns\TextColumn; TextColumn::make('rating') ->summarize(Average::make()->numeric( locale: 'nl', ))从源码看,CanFormatState.php 中的numeric()最终调用Illuminate\Support\Number::format()完成本地化数字格式化;同时它还支持decimalSeparator、thousandsSeparator、maxDecimalPlaces等参数,且locale未指定时优先回退到$summarizer->getTable()->getDefaultNumberLocale(),其次才是config('app.locale')。
货币格式化
money()方法可以方便地以任意货币格式化金额:
use Filament\Tables\Columns\Summarizers\Sum; use Filament\Tables\Columns\TextColumn; TextColumn::make('price') ->summarize(Sum::make()->money('EUR'))money()还提供divideBy参数,在格式化之前先将原始值除以指定数。例如数据库中以"分"存储价格时:
use Filament\Tables\Columns\Summarizers\Sum; use Filament\Tables\Columns\TextColumn; TextColumn::make('price') ->summarize(Sum::make()->money('EUR', divideBy: 100))默认使用应用的 locale 格式化货币;同样可以传入locale参数:
use Filament\Tables\Columns\Summarizers\Average; use Filament\Tables\Columns\TextColumn; TextColumn::make('price') ->summarize(Sum::make()->money('EUR', locale: 'nl'))还可以通过decimalPlaces参数自定义小数位数:
use Filament\Tables\Columns\TextColumn; TextColumn::make('price') ->summarize(Sum::make()->money('EUR', decimalPlaces: 3))底层实现见 CanFormatState.php:货币未指定时回退到表格的getDefaultCurrency(),最终通过Number::currency()格式化。
限制文本长度
使用limit()限制汇总值的显示长度:
use Filament\Tables\Columns\Summarizers\Range; use Filament\Tables\Columns\TextColumn; TextColumn::make('sku') ->summarize(Range::make()->limit(5))limit(int $length = 100, ?string $end = '...')内部通过Str::limit()截断文本(见 CanFormatState.php),end参数可自定义省略后缀。
添加前缀或后缀
使用prefix()/suffix()为汇总值添加前缀或后缀,二者都支持Htmlable(如HtmlString),便于插入上标等富文本内容:
use Filament\Tables\Columns\Summarizers\Sum; use Filament\Tables\Columns\TextColumn; use Illuminate\Support\HtmlString; TextColumn::make('volume') ->summarize(Sum::make() ->prefix('Total volume: ') ->suffix(new HtmlString(' m³')) )从 CanFormatState.php 的formatState()可以看出:当前缀/后缀是Htmlable时,渲染会自动切换为 HTML 输出模式;该 trait 还提供placeholder()、html()、markdown()等格式化辅助方法。
自定义汇总器(Custom Summaries)
通过using()方法可以直接返回自定义计算值,无需新建类:
use Filament\Tables\Columns\Summarizers\Summarizer; use Filament\Tables\Columns\TextColumn; use Illuminate\Database\Query\Builder; TextColumn::make('name') ->summarize(Summarizer::make() ->label('First last name') ->using(fn (Builder $query): string => $query->min('last_name')))回调可以访问数据库$query构建器实例进行任意计算,并返回要在表格中显示的值。底层using()将闭包保存在$using属性中,getState()优先执行该闭包(见 Summarizer.php)。
如果希望复用一段自定义汇总逻辑,更规范的做法是继承Summarizer基类并重写summarize(Builder $query, string $attribute)方法(基类默认返回null,见 Summarizer.php),同时可以像内置汇总器一样覆写getDefaultLabel()提供默认标签。
条件隐藏汇总(Conditionally hiding the summary)
向hidden()方法传入布尔值或返回布尔值的函数即可隐藏汇总。闭包可以通过$query参数访问该汇总器的 Eloquent 查询构建器实例:
use Filament\Tables\Columns\Summarizers\Summarizer; use Filament\Tables\Columns\TextColumn; use Illuminate\Database\Eloquent\Builder; TextColumn::make('sku') ->summarize(Summarizer::make() ->hidden(fn (Builder $query): bool => ! $query->exists()))visible()方法则达到相反效果:
use Filament\Tables\Columns\Summarizers\Summarizer; use Filament\Tables\Columns\TextColumn; use Illuminate\Database\Eloquent\Builder; TextColumn::make('sku') ->summarize(Summarizer::make() ->visible(fn (Builder $query): bool => $query->exists()))从 CanBeHidden.php 的源码可以看到,可见性判断会基于当前查询 SQL 做结果缓存(visibilityCache),且当传入query()查询时,getSummarizers($query)只会返回可见的汇总器(见 CanBeSummarized.php),即不可见的汇总器连查询都不会执行。
汇总分组行(Summarising groups of rows)
汇总可以配合分组(grouping)使用:只要在分组表格的列上添加汇总器,Filament 会自动为每个分组内部的记录渲染汇总,无需额外配置。
仅显示分组汇总、隐藏分组明细行
使用groupsOnly()方法可以隐藏分组内的数据行、只展示每个分组的汇总结果,这在报表类场景中非常实用:
use Filament\Tables\Columns\Summarizers\Sum; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Table; public function table(Table $table): Table { return $table ->columns([ TextColumn::make('views_count') ->summarize(Sum::make()), TextColumn::make('likes_count') ->summarize(Sum::make()), ]) ->defaultGroup('category') ->groupsOnly(); }上述示例会按category分组,并仅显示每个分类下views_count与likes_count的求和结果。
隐藏汇总行(Hiding summary rows)
默认情况下,只要列上存在汇总器,当前页汇总行与全表总计汇总行都会显示。你可以通过表格的summaries()方法控制哪些汇总行出现(该方法定义在 packages/tables/src/Table/Concerns/CanSummarizeRecords.php):
use Filament\Tables\Table; public function table(Table $table): Table { return $table ->summaries( pageCondition: false, allTableCondition: false ); }pageCondition控制"当前页(this page)"汇总行是否显示;allTableCondition控制"全表总计(all data)"汇总行是否显示。
这在以下场景中很有价值:使用分组汇总时只想按组展示汇总;或者当前页汇总冗余、只需全量总计时。
小结
Filament 的汇总机制以"列为中心":通过summarize()将Average、Count、Range、Sum四类内置汇总器(或自定义Summarizer)挂载到任意列上,所有聚合计算均下沉到数据库执行,并自动区分当前页与全表两个统计层级。配合query()作用域、numeric()/money()/limit()/prefix()/suffix()等格式化能力,以及分组汇总与summaries()行级开关,开发者可以快速构建从简单求和到复杂报表的各类统计场景。
进一步阅读:表格列基础用法见 packages/tables/docs/01-overview.md,分组功能见 packages/tables/docs/07-grouping.md,图标列与icons()计数的配合见 packages/tables/docs/02-columns/09-icon.md,汇总器全部源码位于 packages/tables/src/Columns/Summarizers。
【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps & admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考