typetimestamp配置仅在写入时转换类型,读取时返回原始整型值;需用getcreatetimeattr访问器或append虚拟字段实现读取格式化,toarray()中type格式才生效。

typeTimestamp 配置不会让读取结果自动变成字符串日期,它只在写入时做转换——这是升级 TP8 后最常踩的坑。
为什么 typeTimestamp 不影响读取结果
TP8 仍延续「存储类型转换」和「输出格式化」分离的设计。字段设为 'create_time' => 'timestamp' 或 'timestamp:Y-m-d',仅表示:写入时把字符串/时间戳转成整型存库;读取时数据库返回什么,模型就原样给你什么(比如 1725552480)。它不是访问器,也不触发序列化逻辑。
- 若你期望
$user->create_time是"2024-09-05",必须额外加访问器 -
type配置中的格式(如timestamp:Y-m-d)只在toArray()、jsonSerialize()等序列化场景生效,且需配合visible/append才能暴露 - 直接访问属性(
$user->create_time)永远拿到原始整型值
用 getCreateTimeAttr 实现读取时格式化
这是最轻量、最可控的方式,无需改全局配置,每个字段可独立定制:
class User extends Model
{
protected $type = [
'create_time' => 'timestamp',
];
public function getCreateTimeAttr($value, $data)
{
return $value ? date('Y-m-d', (int)$value) : null;
}
}
- 访问器名必须严格为
get + 字段名首字母大写 + Attr(如字段是update_time,则叫getUpdateTimeAttr) - 不要在访问器里调用
$this->getAttribute('create_time'),会无限递归 - 务必判空,否则
null或0传给date()会变成 1970-01-01
需要同时暴露原始值和格式化值怎么办
别覆盖原字段,用 append 新增虚拟字段:
class User extends Model
{
protected $append = ['create_time_text'];
public function getCreateTimeTextAttr($value, $data)
{
return $data['create_time'] ? date('Y-m-d H:i', (int)$data['create_time']) : null;
}
}
- 原始
$user->create_time仍是整型,供前端计算或排序 -
$user->create_time_text是格式化后的字符串,专用于展示 - 序列化时两者都会出现(除非显式
hidden掉其中一个)
注意 TP8 的 toArray() 自动格式化陷阱
如果你用了 'create_time' => 'timestamp:Y-m-d' 这类带格式的 type 配置,它只在 toArray() 中生效,且依赖字段在 visible 或未被 hidden 掉:
-
$user->toArray()会把create_time输出为"2024-09-05" -
$user->create_time单独访问仍是整数 —— 这个不一致最容易让人困惑 - 如果字段被
protected $hidden = ['create_time'];,那toArray()里也看不到它,哪怕有type配置
真正容易被忽略的是:访问器和 type 格式化是两套机制,混用时行为不叠加,得按需选一种主路径。











