必须区分存储层类型转换与展示层格式化:模型$type配置仅影响读取时自动转类型(如'integer'),Db直查不生效;JSON字段需同时配置$json和$jsonType才自动解码;时间字段用'datetime'返回Carbon实例;格式化输出须用getAttr访问器,不可依赖$type;序列化统一键名需重写toArray()。

ThinkPHP 模型字段类型转换与格式化【规范】要求你明确区分「存储层类型转换」和「展示层格式化」,否则查出来的数据仍是字符串、API 返回时间变成空对象、JSON 输出金额精度丢失——这些都不是框架 bug,而是没按规范分层处理。
模型字段读取时自动转类型(type 配置)
这一步只在模型实例读取数据时生效,比如 $user->status、$user->toArray(),Db 类直查完全不触发。
第一步:在模型类中定义 protected $type = [],键必须严格匹配数据库字段名(含下划线),值用全小写标识符:【'status' => 'integer'】,不是 'int' 或 'INT';【'price' => 'float'】,不是 'double';【'data' => 'json'】 仅声明类型,不启用自动编解码。
第二步:JSON 字段要真正自动 decode 成数组,必须同时配置 protected $json = ['data'] 和 protected $jsonType = ['data' => 'array'];只写 $type['data'] = 'json',查出来还是字符串。
立即学习“PHP免费学习笔记(深入)”;
第三步:时间字段优先用 'datetime' 而非 'timestamp'——前者返回 Carbon 实例(支持 format()),后者返回整型时间戳(非法值如空字符串会静默转成 0)。
注意:$type 配置对 write 操作无效,写入时传 '19.90' 字符串,框架不会帮你转 float 再入库,它直接绑定为字符串参数。
模型字段输出时格式化(getAttr 访问器)
想让 API 返回 "2024-05-20" 或 "¥199.00",不能靠 $type,必须用 getAttr 方法做展示层转换。
方法一:直接重写获取器,返回格式化字符串
public function getCreateTimeAttr($value) { return $value ? date('Y-m-d H:i:s', (int)$value) : ''; }
注意:若字段是 datetime 类型,$value 是 Carbon 对象,直接调用 $value->format('Y-m-d') 即可,别用 strtotime() 再解析。
方法二:分离原始值与格式化值,避免覆盖原字段
protected $hidden = ['create_time'];
protected $append = ['create_time_formatted'];
public function getCreateTimeFormattedAttr() { return $this->attributes['create_time']?->format('Y-m-d H:i:s') ?: ''; }
这一步操作起来很简单,但必须确保 append 中的字段名与访问器方法名严格对应:append = ['price_formatted'] → 方法名必须是 getPriceFormattedAttr,少一个字母或大小写错都不触发。
模型序列化时统一控制字段键名与值
前端要 snake_case,但数据库字段是 create_time,又不想改所有 SQL 查询?那就重写模型的 toArray() 方法。
public function toArray() {
$data = parent::toArray();
return array_change_key_case($data, CASE_LOWER);
}
关联模型(如 with('profile'))默认不走这个逻辑,必须让所有子模型都继承同一基类,且基类已重写 toArray(),否则主模型小写了,关联模型还是驼峰,字段风格混杂。
toJson() 会调用 toArray(),所以改这里就一并生效;但要注意:如果某个字段在 getAttr 里返回了对象(如 Carbon),而没在 toJson() 前干预,PHP 默认 json_encode 会把它转成时间戳或报错——这不是 TP 的问题,是 PHP 原生行为。
JSON 字段写入与读取避坑要点
TP6 必须显式配置 $json 才能自动 encode/decode,TP8 虽默认解码,但行为不稳定,统一按 TP6 方式配置最稳妥。
写入时直接赋数组:$model->extra = ['tags' => ['php', 'tp']];
千万别手动 json_encode 后再赋值,否则双重编码。
读取时用 $model->extra 得到的是数组,用 $model->getData('extra') 拿到的是原始 JSON 字符串——这是唯一能拿到未 decode 值的方式,调试时很有用。
如果前端 POST 过来的是 JSON 字符串(如 '{"name":"a"}'),必须先 json_decode($input['extra'], true) 再塞进 data(),否则 TP 会把它当字符串再 encode 一次。



















