laravel 的 casts 不支持 'decimal' 字符串,仅支持 'decimal:10,2' 这类带精度参数的格式,且依赖数据库字段为 decimal 类型;否则需用 'string' cast 配合 bcmath 手动处理精度。

为什么 casts 设为 'decimal' 没用?
因为 Laravel 的 casts 数组里根本**不支持 'decimal' 这个类型**。它只认 PHP 原生类型(如 'float'、'string')或 Eloquent 内置的扩展类型(如 'datetime'、'json'),但没有 'decimal'。直接写进去,Laravel 会忽略或报错,小数照样被截断或转成科学计数法。
正确做法:用 'decimal:<digits>,<places>'</places></digits> 格式
Laravel 实际支持的是带参数的 decimal 字符串格式,必须明确指定总位数和小数位数。这个写法只在 casts 中生效,且依赖数据库字段本身是 DECIMAL 类型。
-
casts中写成'price' => 'decimal:10,2',表示最多 10 位数字、其中 2 位小数 - 对应数据库字段必须是
DECIMAL(10,2),否则精度无法保证 - 该 cast 仅影响模型读取时的类型转换(自动转为 string 或 float,取决于底层驱动),不改变写入行为
- MySQL 驱动下通常转为
string以避免浮点误差;SQLite 可能转为float,需留意精度丢失
写入时仍可能丢精度?检查这三处
即使 cast 正确,小数仍乱,大概率卡在这几个环节:
- PHP 变量本身是
float类型(比如$price = 19.99),参与运算后产生二进制浮点误差,如0.1 + 0.2 !== 0.3 - 数据库迁移没设对精度:
$table->decimal('price', 8, 2)写成$table->decimal('price', 8, 0)就全丢了小数 - 前端传参或中间件做了隐式类型转换,比如用
request()->input('price')接收到的是字符串"19.9900",但没 trim 或验证就直接赋值,可能触发隐式 float 转换
更稳妥的方案:用 string cast + 手动格式化
如果业务对金额、汇率等精度极其敏感,别依赖自动 cast。直接存为字符串,读写都可控:
立即学习“PHP免费学习笔记(深入)”;
- 在
casts中设为'price' => 'string' - 入库前用
number_format($value, 2, '.', '')或bcadd($value, '0', 2)确保两位小数 - 读取后若需计算,用
bcmul/bcadd等 BCMath 函数,避免 float 参与 - 配合
mutator封装逻辑,例如setPriceAttribute($value)统一处理输入
decimal cast 看似省事,但底层没做 BCMath 保护,真正要稳,得自己把住入口和计算环节。











