Filament Infolists KeyValueEntry 详解:键值对数据的表格化展示与列标题定制
【免费下载链接】filamentA powerful open-source UI framework for Laravel • Build and ship apps & admin panels fast with Livewire项目地址: https://gitcode.com/GitHub_Trending/fi/filament
KeyValueEntry 是 Filament Infolists 组件库中用于展示"一维键值对数据"(如 JSON 对象或 PHP 关联数组)的专用 Entry。本文基于仓库中的官方文档 packages/infolists/docs/07-key-value-entry.md,结合 KeyValueEntry.php 源码实现与 KeyValueEntryTest.php 测试用例,系统讲解其基本用法、Eloquent 数据落库方式、keyLabel()/valueLabel()列标题定制(含闭包动态计算),以及底层渲染与空状态行为,帮助你在一站式详情页(Infolist)中专业地呈现元数据、SEO 标签、设置项等键值型数据。
什么是 KeyValueEntry
KeyValueEntry 允许你从一个一维的 JSON 对象 / PHP 数组中渲染出键值对数据。它的典型使用场景包括:文章元信息(meta)、Open Graph / SEO 标签、用户自定义设置、环境变量等"键-值"结构的数据展示。
在 07-key-value-entry.md 中,基础用法如下:
use Filament\Infolists\Components\KeyValueEntry; KeyValueEntry::make('meta')make()接收的参数meta是状态路径(state path),即从当前 Infolist 绑定的数据中取出的字段名。例如,当绑定的记录包含如下meta数据时,Entry 会将其渲染为一张两列的表格:
[ 'description' => 'Filament is a collection of Laravel packages', 'og:type' => 'website', 'og:site_name' => 'Filament', ]渲染结果中,数组的键(如description、og:type)显示在左列,对应的值显示在右列,即默认列标题为Key与Value(可自定义,见下文)。
在完整的 Infolist 上下文里,KeyValueEntry 通常作为infolist()方法中的组件使用,例如在 Resource 的ViewRecord页面中展示记录详情(参见 docs/03-resources/01-overview.md 中的 Infolist 用法):
use Filament\Infolists\Infolist; use Filament\Infolists\Components\KeyValueEntry; public static function infolist(Infolist $infolist): Infolist { return $infolist ->schema([ // ... KeyValueEntry::make('meta'), ]); }在 Eloquent 模型中保存数组数据
官方文档特别强调:如果你将键值对数据保存在 Eloquent 模型中,必须为该属性添加array类型的 Laravel 数组 / JSON 转换(cast),否则从数据库读取后拿到的是字符串而非数组,KeyValueEntry 将无法正确迭代渲染:
use Illuminate\Database\Eloquent\Model; class Post extends Model { /** * @return array<string, string> */ protected function casts(): array { return [ 'meta' => 'array', ]; } // ... }这样数据库中以 JSON 字符串存储的meta字段,在读取时会被自动反序列化为 PHP 关联数组,从而满足 KeyValueEntry 对一维键值数组的输入要求。
定制键列的标题:keyLabel()
默认情况下,键列(左列)的标题是翻译文件中的Key。你可以通过keyLabel()方法将其替换为更贴合业务语义的文案,例如"属性名(Property name)":
use Filament\Infolists\Components\KeyValueEntry; KeyValueEntry::make('meta') ->keyLabel('Property name')keyLabel() 的动态计算
keyLabel()不仅支持静态字符串,还接受一个闭包(Closure)来动态计算标题。闭包中可以注入 Filament 提供的各种工具(Utility Injection),例如$record(当前记录)、$state(组件状态)、$component(组件实例)等参数:
use Filament\Infolists\Components\KeyValueEntry; KeyValueEntry::make('meta') ->keyLabel(fn ($record): string => $record->isPublished() ? 'Published property' : 'Property name')从 KeyValueEntry.php 的源码可以看到,keyLabel()的签名是keyLabel(string | Closure | null $label),返回static(支持链式调用),底层将其存入受保护的$keyLabel属性。
定制值列的标题:valueLabel()
与keyLabel()对称,valueLabel()用于定制值列(右列)的标题:
use Filament\Infolists\Components\KeyValueEntry; KeyValueEntry::make('meta') ->valueLabel('Property value')同样地,valueLabel()也接受闭包进行动态计算,可以注入 Infolist Entry 相关的工具参数:
KeyValueEntry::make('meta') ->valueLabel(fn (): string => 'Property value')两个方法配合使用即可完全重命名表格的两个列头,这在展示多语言界面、或需要更明确的业务术语(如"设置项 / 设置值")时非常实用。
默认标题的翻译回退机制
如果你没有调用keyLabel()/valueLabel(),组件会回退到语言包中的默认翻译。从 KeyValueEntry.php 的getKeyLabel()/getValueLabel()实现可以看到:
public function getKeyLabel(): string { return $this->evaluate($this->keyLabel) ?? __('filament-infolists::components.entries.key_value.columns.key.label'); } public function getValueLabel(): string { return $this->evaluate($this->valueLabel) ?? __('filament-infolists::components.entries.key_value.columns.value.label'); }即:闭包计算结果(或静态值)为空时,自动使用filament-infolists::components.entries.key_value.columns.key.label与...columns.value.label对应的翻译。在英文语言包 components.php 中,默认值分别为Key和Value。这意味着只要你的应用发布了 Filament 的语言资源,列标题就会自动随语言切换。
源码视角:KeyValueEntry 是如何渲染的
要深入理解 KeyValueEntry 的能力边界,需要看它的核心渲染方法。KeyValueEntry 实现了HasEmbeddedView接口,通过 toEmbeddedHtml() 直接内联输出 HTML,而不是依赖独立的 Blade 视图文件:
public function toEmbeddedHtml(): string { $state = $this->getState(); if ($state instanceof Collection) { $state = $state->all(); } $attributes = $this->getExtraAttributeBag() ->class([ 'fi-in-key-value', ]); ob_start(); ?> <table <?= $attributes->toHtml() ?>> <thead> <tr> <th scope="col"><?= e($this->getKeyLabel()) ?></th> <th scope="col"><?= e($this->getValueLabel()) ?></th> </tr> </thead> <tbody> <?php foreach (($state ?? []) as $key => $value) { ?> <tr> <th scope="row"><?= e($key) ?></th> <td><?= e($value === null || is_scalar($value) || ($value instanceof Stringable) || ($value instanceof Htmlable) ? $value : json_encode($value)) ?></td> </tr> <?php } ?> <?php if (empty($state)) { ?> <tr> <td colspan="2" class="fi-in-placeholder"> <?= e($this->getPlaceholder()) ?> </td> </tr> <?php } ?> </tbody> </table> <?php return $this->wrapEmbeddedHtml(ob_get_clean()); }这段实现揭示了几个重要行为:
- 支持
Collection输入:如果状态是Illuminate\Support\Collection,会先通过->all()转为数组再迭代; - 转义输出:键和值都经过
e()(HTML 实体转义)处理,防止 XSS; - 嵌套值的 JSON 序列化:当某个值既不是
null、标量(scalar)、Stringable或Htmlable时(例如嵌套数组),会被json_encode成 JSON 字符串展示,而不是让渲染崩溃。这一点在测试用例renders nested array values as JSON instead of crashing(见 KeyValueEntryTest.php)中得到了验证:['theme' => ['mode' => 'dark']]会渲染出{"mode":"dark"}; - 空状态占位:当数组为空时,渲染一行
colspan="2"的占位单元格,文案来自placeholder()(默认值见下文); - 无障碍语义:表头使用
th scope="col",每行键使用th scope="row",符合表格语义化要求。
Entry 基类提供的基础能力
KeyValueEntry 继承自 Entry.php,因此自动获得了 Entry 家族的所有通用能力:
- 标签(Label):通过
label()设置组件标签,未设置时根据字段名自动生成(如meta→Meta); - 提示与说明:
hint()(右上角提示文本)、helperText()(辅助说明); - Tooltip:
tooltip()鼠标悬停提示; - 对齐方式:
alignment()控制内容对齐; - URL / Action:
url()、action()让整个 Entry 可点击跳转或触发动作; - 内容插槽:
aboveLabel()、belowLabel()、beforeLabel()、afterLabel()、aboveContent()、belowContent()、beforeContent()、afterContent()可在标签或内容周围注入额外组件(Action、ActionGroup 等); - 状态注入:
state()/getStateUsing()可以手动注入或计算状态,例如:
KeyValueEntry::make('meta') ->state(fn ($record) => $record->settings);另外值得注意的是isDehydrated()返回false(Entry.php),即 Entry 组件在表单提交时不会脱水写入,它纯粹是只读展示组件。
定制空状态占位文案:placeholder() 与 emptyMessage()
当键值对数据为空数组时,表格会渲染一行占位文本。在 setUp() 中,组件默认将占位文案设置为语言包中的filament-infolists::components.entries.key_value.placeholder,英文环境下即No entries(见 components.php)。
你可以通过通用的placeholder()方法覆盖它:
use Filament\Infolists\Components\KeyValueEntry; KeyValueEntry::make('meta') ->placeholder('No metadata available')此外,源码中保留了历史方法emptyMessage(),它目前标记为@deprecated,内部只是转发给placeholder()(KeyValueEntry.php):
/** * @deprecated Use `placeholder()` instead. */ public function emptyMessage(string | Closure | null $message): static { $this->placeholder($message); return $this; }测试用例can use deprecated emptyMessage() as an alias for placeholder()(KeyValueEntryTest.php)也确认了该别名行为。新项目中建议统一使用placeholder()。
测试验证:KeyValueEntry 的行为保障
仓库在 tests/src/Infolists/Components/KeyValueEntryTest.php 中为 KeyValueEntry 提供了完整的测试覆盖,可作为实际使用行为的权威参考:
| 测试场景 | 验证点 |
|---|---|
can render | 传入['name' => 'John Doe', 'email' => 'john@example.com']时,页面成功渲染且能看到键和值文本 |
renders nested array values as JSON instead of crashing | 嵌套数组值被 JSON 序列化展示,不抛异常 |
can render with custom key and value labels | keyLabel('Setting')+valueLabel('Value')后列标题正确显示 |
can set and get keyLabel() / valueLabel() | 设置后可读取,且返回一致的字符串 |
getKeyLabel() / getValueLabel()未设置时 | 返回翻译包默认值Key/Value |
can set keyLabel() / valueLabel() using a Closure | 闭包动态计算标题生效 |
returns fluent $this from keyLabel() | 方法支持链式调用 |
can use deprecated emptyMessage() | 作为placeholder()的别名生效 |
测试中还通过 Livewire 测试组件(如TestComponentWithKeyValueEntry)使用Schema->state([...])->components([...])的方式注入状态并断言渲染结果,这为我们手写 Infolist 时如何组织状态与组件提供了清晰的范例。
与 KeyValue 表单字段的呼应
如果你需要在创建 / 编辑表单中编辑这类键值对数据,Filament Forms 包提供了对应的KeyValue字段(见 packages/forms/docs/16-key-value.md)。它同样以"Key / Value"两列的形式录入数据,存储结构与 KeyValueEntry 期望的一维数组格式一致。因此常见的组合方案是:编辑页用KeyValue字段录入,详情页用KeyValueEntry只读展示,两者共享同一份数组类型的数据结构,配合 Eloquent 的arraycast 即可形成完整的读写闭环。
总结
KeyValueEntry 是 Filament Infolists 中展示一维键值数据的标准方案,其要点可归纳为:
- 基本使用:
KeyValueEntry::make('meta')即可渲染键值表格,输入为关联数组或 JSON 对象,Eloquent 场景记得加arraycast; - 列标题定制:
keyLabel()与valueLabel()支持静态字符串与闭包动态计算,未设置时自动回退到语言包翻译; - 健壮的渲染实现:源码内联渲染表格,自动转义输出、处理
Collection、将嵌套值 JSON 序列化、并提供空状态占位; - 空状态定制:通过
placeholder()覆盖默认的No entries,emptyMessage()为已废弃别名; - 通用 Entry 能力:继承自 Entry 基类,可自由叠加标签、提示、Tooltip、对齐、URL / Action 与内容插槽等能力。
如需深入源码,可继续阅读 KeyValueEntry.php、Entry.php 与对应的 测试文件;若想了解整个 Infolists 组件家族,可参考 packages/infolists/docs/01-overview.md。
【免费下载链接】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),仅供参考