Yii2中int字段存为字符串的根源是PDO默认将所有数值fetch为string,再经Yii类型推导处理失败;需在DB配置中关闭ATTR_STRINGIFY_FETCHES和ATTR_EMULATE_PREPARES,或在赋值时强制类型转换。

Yii2 更新数据时 int 字段被存成字符串,不是模型写错了,而是 PDO 默认把所有数值都 fetch 成 string,再经 Yii 的类型推导逻辑二次处理——尤其当字段是 TINYINT(1) 或无符号整型(如 INT UNSIGNED)时,PHP 无法安全映射为 int,就干脆全扔成 string。
为什么 update() 后数据库里 int 变成了字符串?
这不是 update() 本身的问题,而是你传进来的数据类型已经不对了。常见源头:
- 前端传参没做类型转换,
$_POST['status']是"1"(字符串),直接塞给$model->status,save() 时 PDO 写入的就是字符串字面量 - 用
asArray(true)查询后改值再保存,数组里的字段仍是 string,没经过 ActiveRecord 的属性赋值流程,类型丢失 - 手动调用
$model->setAttribute('id', '123'),但没 cast,ActiveRecord 不会自动转类型
关键点:save() 仅对模型声明的 schema 类型做一次 cast(比如把 "1" 转成 int),但前提是字段在 getTableSchema() 中被正确识别为 integer,且 PHP 能安全表示该值(比如不超 PHP_INT_MAX)。
PDO 层面:全局关闭 stringify_fetches 最有效
在 config/db.php 的 DB 组件配置中加这两项,能从根源上让 PDO 返回原生类型:
'attributes' => [
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES => false,
]
注意:
- 必须同时关掉
PDO::ATTR_EMULATE_PREPARES,否则STRINGIFY_FETCHES不生效 - 某些旧版 MySQL 驱动(如 mysqlnd
- 开启后,嵌套事务可能失败(Yii2 原生
Transaction依赖 emulate),需继承重写Connection和Transaction类(见知识库中ConnectionExt示例)
模型层:绕过 schema 误判,手动修正类型
当数据库字段是 TINYINT(1) 却存状态码(0/1/2),Yii 会强行当 boolean 处理,导致 update 时 true → 1、false → 0,中间值 2 永远写不进去。解决方式:
- 建表时改用
TINYINT(2)或SMALLINT,避免触发 Yii 的 boolean 自动识别 - 无法改库?在模型里覆盖
getTableSchema():
public function getTableSchema()
{
$schema = parent::getTableSchema();
$schema->columns['status']->type = 'integer';
return $schema;
}
这样 save() 时类型校验和 cast 都按 integer 走,不会丢值。
更新前强制类型转换最稳妥
不依赖 PDO 或 schema 配置,直接在赋值时控制输入类型:
- 接收参数后立刻 cast:
$model->status = (int) $request->post('status'); - 重写
rules()加'status', 'filter', 'filter' => 'intval' - 在
beforeSave()里统一处理:$this->status = (int) $this->status;
尤其适合状态字段、ID、排序值等明确应为整数的场景。比依赖框架自动转换更可控,也避开 PHP_INT_SIZE === 4 导致的无符号整型 fallback 到 string 的坑。
真正难搞的不是怎么转,而是哪一层该负责转换——PDO 配置管读取,schema 管识别,模型规则管输入,beforeSave 管兜底。漏掉任意一环,int 就悄悄变 string。


















