thinkphp中可通过getfullnameattr访问器定义只读虚拟字段fullname,由first_name和last_name拼接而成;需用visible或append显式输出,不可用于查询/排序/写入。

ThinkPHP 的模型字段不能直接设为“只读虚拟字段”,但可以通过 get 类型的访问器(accessor)+ set 类型的修改器(mutator)控制读写行为,再配合 hidden 或 visible 控制输出。所谓“多个真实字段合成返回”,本质是定义一个只读计算字段,不存入数据库,仅用于读取时动态拼接。
怎么定义只读的虚拟字段(如 full_name)
在模型中用 getFullnameAttr 方法定义访问器,返回由 first_name 和 last_name 拼接的结果。注意方法名必须是驼峰式、以 get 开头、Attr 结尾,且对应字段名转驼峰(full_name → getFullnameAttr):
class User extends Model
{
protected $schema = [
'id' => 'int',
'first_name' => 'string',
'last_name' => 'string',
];
public function getFullnameAttr($value, $data)
{
return trim($data['first_name'] . ' ' . $data['last_name']);
}
}
这样调用 $user->fullname 就能拿到拼接结果;但该字段默认不会出现在 $user->toArray() 中,需显式声明:
- 加到
visible数组里:protected $visible = ['id', 'fullname']; - 或用
append动态追加:$user->append(['fullname']) - 切勿在
$schema或数据库中定义fullname字段,否则会触发类型检测错误
为什么不能用 setFullnameAttr 实现“只写”或“禁止写入”
setXxxAttr 是在数据写入前触发的,它改的是即将入库的值,不是模型属性本身。如果你只想禁止写入 fullname,最稳妥的方式是:不定义 setFullnameAttr,并在赋值时拦截:
- 模型中重写
__set方法可拦截非法赋值,但会影响所有字段,不推荐 - 更实用的做法是:文档/团队规范明确该字段只读,并在控制器层避免
$user->fullname = ...这类操作 - 若真要强约束,可在模型的
save前用getData()检查是否含fullname键,有则抛异常
ThinkPHP 不提供字段级“write_only: false”配置,别指望靠配置项屏蔽写入。
多个字段合成时要注意空值和性能
拼接逻辑看似简单,但线上容易出空格错位、NPE、SQL 注入(如果拼的是用户输入字段)等问题:
- 用
trim()+?? ''防空:($data['first_name'] ?? '') . ' ' . ($data['last_name'] ?? '') - 避免在访问器里查数据库(比如拼接部门名称需关联查询),这会导致 N+1;应改用关联模型 +
with预载 - 如果该字段高频使用且拼接逻辑复杂,考虑在数据库视图或冗余字段中预计算,而非每次 PHP 层拼
- 注意 JSON 序列化时,
append字段默认不参与,需手动toJson(['fullname'])
真正麻烦的不是怎么写这个访问器,而是后续所有地方都得意识到:这个字段不是真实字段,不能用于 where 查询、不能用于 order 排序、不能被 save 写入——这些限制不会报错,只会静默失效。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











