字段概述
字段是 Payload 的基本构建模块,了解如何添加或删除字段、更改字段类型、添加钩子、定义访问控制和验证。
字段是 Payload 的基础构建模块。它们定义了将存储在数据库中的文档结构,并自动在管理后台中生成对应的用户界面。
Payload 提供了多种字段类型可供选择,从简单的文本字符串到嵌套数组和区块等。大多数字段会将数据保存到数据库,而有些则仅用于展示。字段可以配置自定义验证、条件逻辑、访问控制、钩子等多种功能。
字段的外观和行为可以无限定制,而不会影响其底层数据结构。通过使用自定义字段组件,字段设计能够承受重大修改甚至完全替换。
要在你的集合或全局配置中配置字段,请使用 fields
属性:
import type { CollectionConfig } from 'payload'
export const Page: CollectionConfig = {
// ...
fields: [
// highlight-line
// ...
],
}
字段类型
Payload 提供了多种内置的字段类型,每种类型都有其独特的属性和行为,决定了它可以接受哪些值、如何在 API 中呈现以及如何在管理面板中渲染。
要配置字段,请在你的集合或全局配置中使用 fields
属性:
import type { CollectionConfig } from 'payload'
export const Page: CollectionConfig = {
slug: 'pages',
// highlight-start
fields: [
{
name: 'field',
type: 'text',
},
],
// highlight-end
}
提醒: 每个字段都是一个至少包含 type
属性的对象。该属性将字段与其对应的字段类型匹配。更多详情。
Payload 中的字段主要分为三大类:
开始编写字段时,首先确定哪种字段类型最适合你的应用。然后使用所选字段类型的字段选项来编写你的字段。
数据字段
数据字段用于在数据库中存储数据。所有数据字段都有一个 name
属性,这是用于存储字段值的键名。
以下是可用的数据字段类型:
- Array - 用于重复内容,支持嵌套字段
- Blocks - 用于基于块的内容,支持嵌套字段
- Checkbox - 存储布尔值 true/false
- Code - 渲染代码编辑器界面并存储字符串
- Date - 渲染日期选择器并存储时间戳
- Email - 确保值是格式正确的电子邮件地址
- Group - 将字段嵌套在键控对象中
- JSON - 渲染 JSON 编辑器界面并存储 JSON 对象
- Number - 存储数值
- Point - 用于位置数据,存储几何坐标
- Radio - 渲染单选按钮组,只允许选择一个值
- Relationship - 建立与其他集合的关系
- Rich Text - 渲染完全可扩展的富文本编辑器
- Select - 渲染下拉/选择列表样式的值选择器
- Tabs (Named) - 类似于 group,但在选项卡布局中渲染嵌套字段
- Text - 简单的文本输入框,存储字符串
- Textarea - 类似于 text,但允许多行输入
- Upload - 允许本地文件和图片上传
展示型字段
展示型字段不会在数据库中存储数据。它们主要用于在管理面板中组织和展示其他字段,或者添加自定义UI组件。
以下是可用的展示型字段:
虚拟字段
虚拟字段用于显示不存储在数据库中的数据。它们适用于通过钩子等方式在API响应中显示计算值。
以下是可用的虚拟字段:
- 关联字段 - 实现字段间的双向数据绑定
字段选项
所有字段至少需要type
属性。该属性将字段与其对应的字段类型匹配,以确定其在管理面板中的外观和行为。每种字段类型根据其自身类型都有独特的选项集。
要设置字段类型,请在字段配置中使用type
属性:
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text', // highlight-line
name: 'myField',
}
完整的配置选项列表,请参阅每个字段类型的文档。
字段名称
所有数据字段都需要一个name
属性。这是用于在数据库中存储和检索字段值的键名。该属性在当前字段的同级字段中必须是唯一的。
要设置字段名称,请在字段配置中使用name
属性:
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text',
name: 'myField', // highlight-line
}
Payload 保留了一些字段名称供内部使用。使用保留字段名称会导致该字段从配置中被清除。
以下字段名称被禁止使用:
__v
salt
hash
file
字段级钩子
除了可以在文档级别定义钩子外,你还可以逐个字段定义极其精细的逻辑。
要定义字段级钩子,请在字段配置中使用hooks
属性:
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text',
name: 'myField',
// highlight-start
hooks: {
// ...
},
// highlight-end
}
有关字段级钩子的完整详情,请参阅字段钩子文档。
字段级访问控制
除了可以在文档级别定义访问控制外,你还可以逐个字段定义极其精细的权限。
要定义字段级访问控制,请在字段配置中使用access
属性:
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text',
name: 'myField',
// highlight-start
access: {
// ...
},
// highlight-end
}
有关字段级访问控制的完整详情,请参阅字段访问控制文档。
默认值
字段可以选择性地预填充初始值。这个功能在 Admin Panel 和 API 请求中都会被使用,用于在 create
或 update
操作期间填充缺失或未定义的字段值。
要设置字段的默认值,可以在 Field Config 中使用 defaultValue
属性:
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text',
name: 'myField',
defaultValue: 'Hello, World!', // highlight-line
}
默认值可以定义为静态值或返回值的函数。当 defaultValue
被静态定义时,Payload 的 Database Adapters 会将其应用到数据库模式或模型中。
函数可以使用以下参数属性:
user
- 已认证的用户对象locale
- 当前选择的语言环境字符串req
-PayloadRequest
对象
以下是 defaultValue
函数的示例:
import type { Field } from 'payload'
const translation: {
en: 'Written by'
es: 'Escrito por'
}
export const myField: Field = {
name: 'attribution',
type: 'text',
// highlight-start
defaultValue: ({ user, locale, req }) =>
`${translation[locale]} ${user.name}`,
// highlight-end
}
提示: 你可以使用异步的 defaultValue
函数,通过 req.payload
从 API 请求或 Local API 获取数据来填充字段。
验证
字段会根据其字段类型和其他字段选项(如 required
或 min
和 max
值约束)自动进行验证。但如有需要,也可以通过提供自定义验证函数来定制或完全替换字段验证逻辑。
要为字段设置自定义验证函数,请在 Field Config 中使用 validate
属性:
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text',
name: 'myField',
validate: (value) => Boolean(value) || '此字段为必填项', // highlight-line
}
自定义验证函数应返回 true
或表示错误信息的 string
,该信息将显示在 API 响应中。
validate
函数接收以下参数:
参数 | 描述 |
---|---|
value | 待验证字段的值 |
ctx | 包含额外数据和上下文的对象。更多详情 |
验证上下文
ctx
参数包含完整的文档数据、同级字段数据、当前操作以及其他有用信息(如当前认证用户):
import type { Field } from 'payload'
export const MyField: Field = {
type: 'text',
name: 'myField',
// highlight-start
validate: (val, { user }) =>
Boolean(user) || '必须登录才能保存此字段',
// highlight-end
}
ctx
对象中提供了以下额外属性:
Property | 描述 |
---|---|
data | 包含当前正在编辑的完整 collection 或 global 文档的对象。 |
siblingData | 包含仅限该字段同一父级下字段范围内文档数据的对象。 |
operation | 根据 UI 操作或 API 调用,值为 create 或 update 。 |
path | 字段在 schema 中的完整路径,表示为字符串段数组,包括数组索引。例如 ['group', 'myArray', '1', 'textField'] 。 |
id | 当前正在编辑文档的 id 。在 create 操作期间 id 为 undefined 。 |
req | 当前 HTTP 请求对象。包含 payload 、user 等。 |
event | 根据当前操作,值为 onChange 或 submit 。用作性能优化选项。更多详情。 |
复用默认字段验证
当使用自定义验证函数时,Payload 会用你的函数替代默认验证。但有时你可能只想在默认验证基础上添加自定义逻辑。
要复用默认字段验证,可以在自定义验证函数中调用它们:
import { text } from 'payload/shared'
const field: Field = {
name: 'notBad',
type: 'text',
validate: (val, args) => {
if (val === 'bad') return 'This cannot be "bad"'
return text(val, args) // highlight-line
},
}
以下是所有默认字段验证函数的列表:
import {
array,
blocks,
checkbox,
code,
date,
email,
group,
json,
number,
point,
radio,
relationship,
richText,
select,
tabs,
text,
textarea,
upload,
} from 'payload/shared'
异步字段验证
根据你的需求,自定义验证函数也可以是异步的。这使得向外部服务发起请求或执行其他异步逻辑成为可能。
在编写异步验证函数时,必须考虑性能影响。验证会在字段每次变更时执行,因此它们应该尽可能轻量。如果你需要执行开销较大的验证(如查询数据库),可以考虑使用 ctx
对象中的 event
属性,仅在表单提交时运行验证。
要编写异步验证函数,使用 async
关键字定义你的函数:
import type { CollectionConfig } from 'payload'
export const Orders: CollectionConfig = {
slug: 'orders',
fields: [
{
name: 'customerNumber',
type: 'text',
// highlight-start
validate: async (val, { event }) => {
if (event === 'onChange') {
return true
}
// 仅在表单提交时执行开销较大的验证
const response = await fetch(`https://your-api.com/customers/${val}`)
if (response.ok) {
return true
}
return '提供的客户编号与我们的记录不匹配。'
},
// highlight-end
},
],
}
自定义 ID 字段
所有 Collections 都会自动生成自己的 ID 字段。如果需要,你可以通过向配置提供显式的 ID 字段来覆盖此行为。该字段应为必填字段,或者具有动态生成 ID 的钩子。
要定义自定义 ID 字段,添加一个顶级字段,并将 name
属性设置为 id
:
import type { CollectionConfig } from 'payload'
export const MyCollection: CollectionConfig = {
// ...
fields: [
{
name: 'id', // highlight-line
required: true,
type: 'number',
},
],
}
管理选项
你可以通过任何 Field Config 的 admin
属性来自定义 Admin Panel 中字段的外观和行为:
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
// highlight-line
// ...
},
},
],
}
以下是可用的选项:
选项 | 描述 |
---|---|
condition | 根据其他字段以编程方式显示/隐藏字段。更多详情。 |
components | 所有 Field Components 都可以替换为你定义的自定义组件。 |
description | 在字段旁边显示的帮助文本,为编辑者提供更多信息。更多详情。 |
position | 通过定义 position: 'sidebar' 来指定字段是否应渲染在侧边栏中。 |
width | 限制字段的宽度。可以传递任何基于字符串的值,如像素、百分比等。当字段嵌套在 Row 类型中并可以水平组织时,此属性特别有用。 |
style | 注入到字段根元素的 CSS 属性。 |
className | 将 CSS 类属性 附加到字段的根 DOM 元素上。 |
readOnly | 将字段设置为 readOnly 对 API 没有任何影响,但会禁用管理组件的可编辑性,防止编辑者修改字段值。 |
disabled | 如果字段被 disabled ,它将完全从 Admin Panel 中省略。 |
disableBulkEdit | 将 disableBulkEdit 设置为 true 可防止字段出现在多文档编辑时的选择选项中。对于 UI 字段默认为 true 。 |
disableListColumn | 将 disableListColumn 设置为 true 可防止字段出现在列表视图列选择器中。 |
disableListFilter | 将 disableListFilter 设置为 true 可防止字段出现在列表视图筛选选项中。 |
hidden | 将字段转换为 hidden 输入类型。其值仍会随 Admin Panel 中的请求提交,但字段本身对编辑者不可见。 |
字段描述
字段描述用于向编辑者提供关于字段的额外信息,例如特殊说明。其展示位置因字段类型而异,但通常会以较为低调的样式显示在字段输入框下方。
描述可以通过三种方式配置:
要为字段添加自定义描述,可在 Field Config 中使用 admin.description
属性:
import type { CollectionConfig } from 'payload'
export const MyCollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
description: 'Hello, world!', // highlight-line
},
},
],
}
描述函数
自定义描述也可以定义为函数形式。描述函数在服务器端执行,可以根据用户当前的语言环境来格式化简单的描述。
要为字段添加描述函数,请在 Field Config 中将 admin.description
属性设置为一个函数:
import type { CollectionConfig } from 'payload'
export const MyCollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
description: ({ t }) => `${t('Hello, world!')}`, // highlight-line
},
},
],
}
所有描述函数都会接收以下参数:
参数 | 描述 |
---|---|
t | 用于国际化管理界面的 t 函数。更多详情 |
注意: 如果你需要在表单中订阅实时更新,请改用描述组件。更多详情。
条件逻辑
你可以通过在每个字段上使用条件逻辑,根据其他字段的状态来显示或隐藏字段。字段的 admin 配置中的 condition
属性接受一个函数,该函数接收以下参数:
参数 | 描述 |
---|---|
data | 当前正在编辑的整个文档数据。 |
siblingData | 仅包含与条件字段直接同级的字段数据。 |
ctx | 包含关于字段位置和用户的附加信息的对象。 |
ctx
对象:
属性 | 描述 |
---|---|
blockData | 最近父级块的数据。如果字段不在块内,则为 undefined 。 |
operation | 表示字段类型当前执行的操作的字符串。 |
path | 字段在 schema 中的完整路径,表示为字符串段的数组,包括数组索引。例如 ['group', 'myArray', '1', 'textField'] 。 |
user | 当前已认证的用户对象。 |
condition
函数应返回一个布尔值,用于控制是否显示该字段。
示例:
{
fields: [
{
name: 'enableGreeting',
type: 'checkbox',
defaultValue: false,
},
{
name: 'greeting',
type: 'text',
admin: {
// highlight-start
condition: (data, siblingData, { blockData, path, user }) => {
if (data.enableGreeting) {
return true
} else {
return false
}
},
// highlight-end
},
},
]
}
自定义组件
在Admin Panel中,字段会在三个不同的位置显示:
要替换为自定义的Field Components,可以在Field Config中使用admin.components
属性:
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
// ...
admin: {
components: {
// highlight-line
// ...
},
},
},
],
}
可用的选项如下:
组件 | 描述 |
---|---|
Field | 在编辑视图中渲染的表单字段。更多详情。 |
Cell | 在列表视图中渲染的表格单元格。更多详情。 |
Filter | 在列表视图中渲染的筛选组件。更多详情。 |
Label | 覆盖Field Component的默认标签。更多详情。 |
Error | 覆盖Field Component的默认错误提示。更多详情。 |
Diff | 覆盖版本差异视图中渲染的默认差异组件。更多详情。 |
Description | 覆盖Field Component的默认描述文本。更多详情。 |
beforeInput | 一组元素,将被添加到Field Component输入框之前。更多详情。 |
afterInput | 一组元素,将被添加到Field Component输入框之后。更多详情。 |
Field
Field 组件是在编辑视图中实际渲染的表单字段。这是用户在编辑文档时会与之交互的输入控件。
要替换为你自己的 Field 组件,可以在 Field Config 中使用 admin.components.Field
属性:
import type { CollectionConfig } from 'payload'
export const CollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
// ...
admin: {
components: {
Field: '/path/to/MyFieldComponent', // highlight-line
},
},
},
],
}
有关如何构建自定义组件的详细信息,请参阅构建自定义组件。
除了替换整个 Field 组件外,你也可以选择性地只替换或插入特定部分,通过使用 Label
、
Error
、beforeInput
和
afterInput
属性来实现。
默认属性
所有 Field 组件默认接收以下属性:
Property | Description |
---|---|
docPreferences | 包含文档 Preferences 的对象。 |
field | 在客户端组件中,这是经过清理的客户端 Field 配置。在服务端组件中,这是原始的 Field 配置。服务端组件还会通过 clientField 属性接收清理后的字段配置(见下文)。 |
locale | 字段的语言环境。更多详情。 |
readOnly | 布尔值,表示字段是否为只读。 |
user | 当前认证用户。更多详情。 |
validate | 用于验证字段的函数。 |
path | 字符串,表示运行时字段的直接动态路径,例如 myGroup.myArray.0.myField 。 |
schemaPath | 字符串,表示 Field 配置的直接静态路径,例如 posts.myGroup.myArray.myField 。 |
indexPath | 连字符分隔的字符串,表示字段在最近的命名祖先字段中的路径,例如 0-0 。 |
除了上述属性外,所有服务端组件还会接收以下属性:
Property | Description |
---|---|
clientField | 可序列化的客户端 Field 配置。 |
field | Field 配置。 |
data | 当前正在编辑的文档。 |
i18n | i18n 对象。 |
payload | Payload 类。 |
permissions | 基于当前认证用户的字段权限。 |
siblingData | 字段同级的数据。 |
user | 当前认证用户。更多详情。 |
value | 渲染时字段的值。 |
表单值的发送与接收
当你替换 Field
组件时,需要自行处理表单中字段值的发送和接收。
为此,可以从 @payloadcms/ui
导入 useField
钩子,并使用它来管理字段值:
'use client'
import { useField } from '@payloadcms/ui'
export const CustomTextField: React.FC = () => {
const { value, setValue } = useField() // highlight-line
return <input onChange={(e) => setValue(e.target.value)} value={value} />
}
完整可用的 React 钩子列表,请参阅 Payload React Hooks 文档。更多帮助信息,请查看构建自定义组件。
TypeScript#field-component-types
在构建自定义字段组件时,可以导入客户端字段属性类型来确保组件的类型安全。每个字段类型都有对应的客户端和服务端组件类型定义。命名惯例是在目标类型前加上字段类型前缀,例如 TextFieldClientComponent
:
import type {
TextFieldClientComponent,
TextFieldServerComponent,
TextFieldClientProps,
TextFieldServerProps,
// ...以及其他字段类型的定义
} from 'payload'
具体类型导入请参考各个字段类型的文档。
Cell
Cell 组件在列表视图(List View)的表格中渲染,用于表示字段在表格单元格中显示的值。
要替换为你自己的 Cell 组件,可以在 Field Config 中使用 admin.components.Cell
属性:
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Cell: '/path/to/MyCustomCellComponent', // highlight-line
},
},
}
所有 Cell 组件都会接收相同的默认字段组件属性,以及以下额外属性:
属性 | 描述 |
---|---|
link | 布尔值,表示该单元格是否应该被包裹在链接中。 |
onClick | 当单元格被点击时调用的函数。 |
关于如何构建自定义组件本身的详细信息,请参阅构建自定义组件。
Filter
Filter 组件是在列表视图(List View)的"筛选依据"下拉菜单中渲染的实际输入元素,用于在构建筛选器时表示该字段。
要替换为你自己的 Filter 组件,可以在 Field Config 中使用 admin.components.Filter
属性:
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Filter: '/path/to/MyCustomFilterComponent', // highlight-line
},
},
}
所有自定义 Filter 组件都会接收相同的默认字段组件属性。
关于如何构建自定义组件本身的详细信息,请参阅构建自定义组件。
Label 组件
Label 组件会在任何需要为字段显示标签的地方渲染。它通常用于编辑视图(Edit View),但也可以在列表视图(List View)和其他地方使用。
要替换为你自己的 Label 组件,可以在 Field Config 中使用 admin.components.Label
属性:
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Label: '/path/to/MyCustomLabelComponent', // highlight-line
},
},
}
所有自定义 Label 组件都会接收相同的默认字段组件属性。
关于如何构建自定义组件本身的详细信息,请参阅构建自定义组件。
TypeScript#label-component-types
在构建自定义 Label 组件时,你可以导入组件类型以确保组件的类型安全。对于每种字段类型和服务器/客户端环境,都有明确的 Label 组件类型。命名约定是在字段类型后附加 LabelServerComponent
或 LabelClientComponent
,例如 TextFieldLabelClientComponent
。
import type {
TextFieldLabelServerComponent,
TextFieldLabelClientComponent,
// ...以及其他字段类型的对应类型
} from 'payload'
描述
除了使用描述属性外,你还可以使用自定义组件作为字段描述。这在需要向用户提供更复杂反馈时非常有用,例如渲染动态字段值或其他交互元素。
要为字段添加描述组件,可以在字段配置中使用 admin.components.Description
属性:
import type { CollectionConfig } from 'payload'
export const MyCollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
components: {
Description: '/path/to/MyCustomDescriptionComponent', // highlight-line
},
},
},
],
}
所有自定义描述组件都会接收相同的默认字段组件属性。
关于如何构建自定义组件本身的详细信息,请参阅构建自定义组件。
TypeScript#description-component-types
在构建自定义描述组件时,你可以导入组件属性以确保组件的类型安全。每种字段类型和服务器/客户端环境都有明确的描述组件类型。命名约定是在字段类型后追加 DescriptionServerComponent
或 DescriptionClientComponent
,例如 TextFieldDescriptionClientComponent
。
import type {
TextFieldDescriptionServerComponent,
TextFieldDescriptionClientComponent,
// 其他字段类型以此类推
} from 'payload'
错误组件
Error 组件在字段验证失败时渲染,通常以醒目的样式显示在字段输入框下方。
要替换为你自己的 Error 组件,可以在 Field Config 中使用 admin.components.Error
属性:
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Error: '/path/to/MyCustomErrorComponent', // highlight-line
},
},
}
所有 Error 组件都会接收默认字段组件属性。
关于如何构建自定义组件的详细信息,请参阅构建自定义组件。
TypeScript#error-component-types
构建自定义 Error 组件时,可以导入组件类型以确保组件的类型安全。每个字段类型和服务器/客户端环境都有对应的显式类型。命名约定是在字段类型后添加 ErrorServerComponent
或 ErrorClientComponent
,例如 TextFieldErrorClientComponent
。
import type {
TextFieldErrorServerComponent,
TextFieldErrorClientComponent,
// 其他字段类型同理
} from 'payload'
差异对比组件
Diff 组件在版本差异视图中渲染,仅在使用版本控制的实体中可见。
要替换为你自己的 Diff 组件,可以在 Field Config 中使用 admin.components.Diff
属性:
import type { Field } from 'payload'
export const myField: Field = {
name: 'myField',
type: 'text',
admin: {
components: {
Diff: '/path/to/MyCustomDiffComponent', // highlight-line
},
},
}
所有 Error 组件都会接收默认字段组件属性。
关于如何构建自定义组件的详细信息,请参阅构建自定义组件。
TypeScript#diff-component-types
在构建自定义差异组件(Diff Components)时,你可以导入组件类型以确保组件的类型安全。每种字段类型和服务端/客户端环境都有明确的组件类型。命名惯例是在字段类型后添加 DiffServerComponent
或 DiffClientComponent
,例如 TextFieldDiffClientComponent
。
import type {
TextFieldDiffServerComponent,
TextFieldDiffClientComponent,
// 其他字段类型同理
} from 'payload'
afterInput 和 beforeInput
通过这些属性,你可以按照名称所示,在输入元素的前后添加多个组件。当你需要在字段旁边渲染额外元素而不替换整个字段组件时,这非常有用。
要在输入元素前后添加组件,可以在字段配置中使用 admin.components.beforeInput
和 admin.components.afterInput
属性:
import type { CollectionConfig } from 'payload'
export const MyCollectionConfig: CollectionConfig = {
// ...
fields: [
// ...
{
name: 'myField',
type: 'text',
admin: {
components: {
// highlight-start
beforeInput: ['/path/to/MyCustomComponent'],
afterInput: ['/path/to/MyOtherCustomComponent'],
// highlight-end
},
},
},
],
}
所有 afterInput
和 beforeInput
组件都会接收相同的默认字段组件属性。
有关如何构建自定义组件的详细信息,请参阅构建自定义组件。
TypeScript
你可以从 payload
包中导入 Payload 的 Field
类型以及其他常见类型。更多详情。
import type { Field } from 'payload'