必须写自定义cast类——当内置cast无法满足加密、json结构校验、时间戳转carbon、值归一化等读写干预需求时;需实现get/set方法,参数通过$parameters传递,且必须用数组语法配置如['name' => [uppercasecast::class]]。

直接用 cast 数组配内置类型不够时,必须写自定义 Cast 类——它不是“可选技巧”,而是处理日期格式化、JSON 结构校验、加密字段等场景的刚需。
什么时候必须自己写 Cast 类
当需要在属性读写过程中做逻辑干预,而内置 Cast(如 as: 'datetime' 或 'array')做不到时:
- 数据库存的是加密字符串,模型属性要自动解密/加密
- 字段是 JSON 字符串,但希望模型里表现为带方法的对象(比如
Address实例) - 存的是时间戳整数,想在模型里暴露为
Carbon实例,但又不走datecast 的默认解析逻辑 - 写入前要做值归一化(如把空数组转为
null,或强制转小写)
Cast 类必须实现的两个方法
自定义 Cast 类必须实现 get 和 set 方法,分别对应从数据库取值和向数据库写值。Laravel 不调用构造函数传参,也不支持依赖注入——所有配置得靠 $parameters 传进来。
-
get($model, $key, $value, $attributes):返回模型属性最终值,$value是原始数据库值 -
set($model, $key, $value, $attributes):返回将被存入数据库的值,$value是赋给模型属性的值 - 如果 Cast 类实现了
serialize方法,还会用于 API 响应序列化(否则用get)
示例:一个把字符串转为全大写的 Cast
class UppercaseCast implements CastsAttributes
{
public function get($model, string $key, $value, array $attributes)
{
return $value;
}
public function set($model, string $key, $value, array $attributes)
{
return strtoupper((string) $value);
}
}
在模型里怎么配才生效
不能只写类名,必须用数组语法传参(哪怕没参数),否则 Laravel 会当作字符串类型名去反射,报 Class does not exist 错误。
- ✅ 正确:
'name' => [UppercaseCast::class] - ✅ 带参数:
'metadata' => [JsonWithValidationCast::class, 'App\Models\Metadata'] - ❌ 错误:
'name' => UppercaseCast::class(会被当成类型字符串处理) - ❌ 错误:
'name' => new UppercaseCast()(Laravel 不接受实例)
参数在 Cast 类的 get/set 方法里通过 $this->parameters 访问(Laravel 自动注入)。
容易被忽略的兼容性细节
自定义 Cast 在 Laravel 9+ 中行为稳定,但在 8.x 及更早版本中,serialize 方法不会被 Eloquent 自动调用——API 响应仍走 get。另外,Eloquent 不会对 Cast 属性做变更检测(isDirty() 可能不准),如果值在 get 中被转换(比如字符串变对象),且该对象没有实现 __toString 或 toArray,序列化时可能出错。
真正麻烦的是测试场景:Cast 类里如果用了模型关联、配置或容器服务,就很难单元测试——它被设计为无状态纯函数式组件,越重逻辑越难维护。











