$casts 不生效的主因是字段名与数据库不一致或类型不匹配;必须确保字段名完全相同(含下划线、大小写),且数据库类型支持对应 cast(如 TINYINT(1) 支持 boolean,VARCHAR 不支持)。

模型里 $casts 不生效?先确认字段名拼写和数据库类型
很多情况不是 $casts 写错了,而是字段在数据库里是 varchar 却想自动转成 boolean,或者字段名在迁移里叫 is_active,但模型里写成了 active。Laravel 只对真实存在的字段做 cast,且严格区分大小写和下划线命名。
-
$casts里的键必须和数据库字段名完全一致(包括下划线、大小写) - MySQL 的
TINYINT(1)能被正确识别为布尔,但CHAR(1)或VARCHAR不行——哪怕存的是'1'或'true' - 如果字段是 JSON 类型,
'json'和'array'都能用,但'array'会额外调用json_decode(..., true),而'json'返回对象
Carbon 实例 vs 字符串:日期字段赋值时别混用
当你给 created_at 赋值 '2024-03-15' 字符串,Laravel 会尝试用 Carbon::parse() 解析;但如果该字段已定义为 'date' 或 'datetime' cast,它不会自动转成 Carbon 实例——只有从数据库读取时才触发 cast。写入时你仍得自己确保类型安全。
- 往模型属性赋值日期字符串前,建议显式用
Carbon::parse('2024-03-15'),避免时区或格式歧义 -
'date'cast 只截取年月日,'datetime'保留完整时间,但两者都要求数据库字段是DATE或DATETIME类型,否则可能丢数据 - 如果数据库字段是
TIMESTAMP,且应用跨时区部署,务必确认config/app.php中的'timezone'与数据库时区一致,否则Carbon输出可能偏移
布尔值从数据库读出来还是 1/0?检查迁移和 cast 是否匹配
常见现象:数据库字段是 TINYINT(1),模型写了 'active' => 'boolean',但 $model->active 还是整数 1。这通常是因为 Laravel 在底层用了 PHP 的 `(bool)` 强转,而 (bool) 1 是 true,但 var_dump 时显示仍为 int(1) ——这是 PHP 类型显示问题,不是 cast 失效。
- 用
var_export($model->active)或gettype($model->active)确认真实类型,不要只看echo或dd()输出 - 如果字段实际存的是字符串
'true'/'false','boolean'cast 无法识别,得用访问器(accessor)手动处理 - MySQL 8.0.16+ 支持原生
BOOLEAN别名,但底层仍是TINYINT(1),所以无需额外适配
自定义 cast 比访问器更稳,但别在 cast 里做 I/O 或复杂逻辑
当 $casts 内置类型不够用(比如需要把数据库里存的逗号分隔字符串转成数组),直接写个 class 实现 Illuminate\Contracts\Database\Eloquent\CastsAttributes 接口比一堆访问器干净得多。但它运行在每次 get/set 属性时,性能敏感路径要小心。
- cast 类的
get()方法接收原始数据库值(可能是 null、string、int),返回最终属性值;set()接收传入值,返回应存入数据库的格式 - 禁止在 cast 里调用
DB::、Http::或任何可能触发数据库查询/网络请求的操作 - 如果逻辑涉及缓存或配置,优先用
app()获取服务,而不是依赖构造注入——cast 实例由框架自动创建,不走容器解析


















