cast类必须实现castsattributes接口并实现get()和set()方法,配置时须用::class语法,带参需通过castable接口和castusing()注入参数,且内部须无状态。

Cast 类必须实现 CastsAttributes 接口,否则报 Class is not a valid cast
直接写个普通类当 Cast 会失败,Laravel 要求显式声明可识别性。不实现 CastsAttributes 或不继承对应抽象类,运行时抛出 Class "App\Casts\CustomJson" is not a valid cast。
- 正确写法:用
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;,然后class CustomJson implements CastsAttributes - 必须同时实现
get()和set()方法,缺一不可;serialize()是可选的(Laravel 9+ 才用) -
set()方法返回值类型要匹配字段预期存储格式:比如 JSON 字段必须返回string或null,返回数组会被静默丢弃 - 不要在
get()里 throw 异常——Eloquent 序列化时会跳过该字段,API 返回里它就“消失”了
模型中配 $casts 必须用 ::class,不能 new 实例或硬编码字符串
配置错一个字符,Cast 就完全不生效,且无提示。最常见错误是把 'payload' => CustomJson::class 写成 'payload' => new CustomJson() 或 'payload' => 'App\Casts\CustomJson'。
-
protected $casts = ['payload' => CustomJson::class]✅ IDE 可跳转、自动补全、类型安全 -
'payload' => 'App\Casts\CustomJson'❌ 硬编码字符串,重构时易断,且 Laravel 不校验类是否存在,直到真正调用才报错 -
'payload' => CustomJson❌ PHP 报“Use of undefined constant”,因为缺::class - 即使 Cast 类没参数,也**必须用数组语法传参**:
'name' => [UppercaseCast::class],否则 Laravel 当作字符串类型名去反射,报Class does not exist
带参数的 Cast 必须用 Castable 接口 + castUsing()
想传深度、单位、密钥等配置?别在构造函数里接参数——Eloquent 实例化 Cast 时不调构造函数,所有参数都靠 $parameters 数组注入。
- 写一个代理类实现
Castable,比如JsonWithDepth,它的castUsing()返回真实 Cast 类名字符串(如JsonDepthCast::class) - 模型里配
'payload' => JsonWithDepth::class,等价于'payload' => JsonWithDepth::castUsing(['depth' => 10]) - 真实 Cast 类(如
JsonDepthCast)里通过$this->parameters['depth'] ?? 512拿参数,不能依赖私有属性存状态——多个字段共用同一个 Cast 实例时会互相污染 - 切记:
castUsing()必须返回字符串类名,不是实例、不是数组、不是self::class
Cast 内部必须无状态、不访问模型关系或未初始化属性
Cast 的生命周期早于模型属性初始化,更早于关系加载。在 get() 或 set() 里调 $model->relation 或 $model->id,轻则 null,重则无限递归。
- 不能查数据库、不能发 HTTP 请求、不能写日志——它只做纯数据转换
- 若需加解密,密钥必须硬编码或从
config()读,不能从$model->encryption_key这种字段取 - 空值处理要主动兜底:
if ($value === null) { return null; },否则json_decode(null)返回null,但json_encode(null)是字符串"null",容易双重编码 - 最易被忽略的一点:Cast 类里**不要有私有属性**。Eloquent 复用实例,
$this->cache这种字段会在不同字段间残留旧值











