$append 是配合访问器触发计算逻辑的开关,需在模型中声明字段名并定义对应 getxxxattr() 方法,仅在模型实例序列化时生效,原生查询不触发,且禁止在获取器中执行数据库操作。

ThinkPHP 的 $append 属性不是“加个字段名就能出值”,而是配合访问器(获取器)触发计算逻辑的开关。它只在模型实例被正确序列化(如调用 toArray()、toJson() 或 API 自动转换)时生效,且每个追加字段都必须有对应命名的 getXXXAttr() 方法。
字段名和获取器必须严格配对
比如要追加 full_name 字段,就得在模型中定义:
- 在
protected $append = ['full_name'];中声明字段名(字符串,小写+下划线) - 同时提供方法
public function getFullNameAttr($value, $data)(驼峰命名,Attr结尾) - 方法内优先读
$data['first_name']和$data['last_name'],而不是$this->first_name,避免重复触发其他获取器 - 返回值必须明确,哪怕只是空字符串:
return $data['first_name'] . ' ' . ($data['last_name'] ?? '');
注意数据来源是否走模型流程
直接用 Db::table()->select() 或 query()->execute() 得到的是原生数组,不会触发任何获取器,$append 完全无效。
- 要用模型查询:如
UserModel::select()或UserModel::find(1) - 集合(Collection)默认不自动执行
$append,需显式调用$list->toArray() - 分页对象(如
Paginator)也需对$list->items()或整个结果调用toArray()
避免性能陷阱:别在获取器里查库或发请求
每个追加字段都会在每条记录的 toArray() 过程中单独执行一次获取器。100 条数据 + 3 个 $append 字段 = 300 次函数调用。
- 禁止在
getXXXAttr()里写User::find()、Db::name()->where()->value()等数据库操作 - 依赖关联数据时,必须提前
with('profile')预加载,再在获取器中用$this->profile?->title - 时间格式化、拼接字符串、状态映射等纯计算逻辑是安全的
静态属性 vs 实例方法:用对场景才不丢字段
protected $append = [...] 是全局默认,所有 toArray() 都生效;而 $model->append([...]) 是单次覆盖,会清空之前所有追加项。
- 通用字段(如
create_time_text)适合写在模型静态属性里 - 接口级临时字段(如仅详情页需要的
is_favorited)应改用实例方法:$user->append(['is_favorited'])->toArray() - 想叠加字段?得手动合并:
$user->append(array_merge($user->getAppend(), ['new_field']))
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











