在thinkphp模型中通过getattr/setattr实现单位换算最轻量可控,如price字段存分为单位、读取时除100返回元,需加is_numeric判断防静默错误;配合$type声明和resource类可提升json序列化与多场景适配可靠性。

模型字段定义时直接绑定单位换算逻辑
ThinkPHP 的模型本身不内置单位换算,但你可以在 getAttr 和 setAttr 方法里做拦截——这是最轻量、最可控的方式。别想着在控制器或视图里统一转,那会重复写、难维护、还容易漏。
常见错误现象:price 字段数据库存的是「分」,但前端总显示成「元」却忘了除 100,或者反过来存错导致金额翻百倍。
- 在模型中重写
getPriceAttr:读取时自动除以 100,返回带两位小数的float或格式化字符串 - 重写
setPriceAttr:接收前端传来的「元」,乘 100 后存整型(避免浮点存储误差) - 如果字段可能为空或非数字,
setPriceAttr里要加is_numeric()判断,否则'' * 100 === 0会静默出错
public function getPriceAttr($value, $data) {
return $value ? round($value / 100, 2) : 0.00;
}
public function setPriceAttr($value) {
return is_numeric($value) ? (int)round($value * 100) : 0;
}
用 Accessor + Mutator 配合类型转换更稳妥
ThinkPHP 6+ 支持在模型属性定义里声明 type 和 cast,配合 getAttr/setAttr 能规避部分类型混乱问题。尤其当字段同时被 JSON 输出、表单提交、Excel 导出多处使用时,这个组合比纯方法拦截更健壮。
使用场景:比如 weight 字段数据库存克(g),但接口要返回「kg」且保留三位小数;导出 Excel 又要求原始克数。
- 定义
weight字段为integer类型,防止数据库存浮点 -
getWeightAttr返回$value / 1000,并用number_format($val, 3)控制精度 - 若需原始值(如导出),可额外定义一个
getRawWeightAttr直接返回$value,避免污染主字段语义 - 注意:不要在
getAttr里做耗时操作(如查库、远程调用),这会拖慢所有查询
JSON 返回时单位字段自动格式化失败?检查序列化配置
模型转 JSON 时,getAttr 方法默认不触发——这是最容易踩的坑。ThinkPHP 默认只对显式获取的属性或 visible/hidden 控制的字段调用访问器,而 JSON 序列化走的是原生 toArray() 流程。
错误现象:API 返回的 {"price": 19900},明明写了 getPriceAttr 却没生效。
- 必须在模型中设置
protected $autoWriteTimestamp = false;(非必需,但常被误配影响调试) - 启用自动转换:在模型顶部加
protected $type = ['price' => 'float'];,再配合getPriceAttr才能进访问器 - 更可靠的做法:重写模型的
toArray(),对关键字段手动调用getData()或__get() - 或者干脆不在 JSON 层依赖访问器,改用资源类(Resource)做最终格式化——适合复杂项目,但小项目杀鸡用牛刀
单位换算逻辑分散在多处?用 Trait 统一收口
当多个模型都有「金额」「重量」「长度」等需要单位换算的字段时,复制粘贴 getXXXAttr 很快就失控。Trait 是 ThinkPHP 里最自然的解耦方式,比继承基类更灵活。
性能影响极小,但要注意命名冲突和加载顺序。
- 新建
app\common\traits\UnitConvert.php,定义通用方法如gToKg()、centToYuan() - 在 Trait 中不直接实现
getAttr,而是提供formatPrice()这类工具方法,由各模型按需调用 - 若字段名固定(如所有金额都叫
amount),可在 Trait 中统一注册getAmountAttr,再用use引入 - 切记:Trait 中不能有构造函数或静态初始化逻辑,否则可能在模型实例化前就执行
真正麻烦的不是怎么写换算,而是单位边界模糊——比如「库存数量」该用个还是千克?「文件大小」存 byte 还是 KB?这些得在数据库设计阶段就钉死,代码层只是补救。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











