MySQL 8.0.16+ boolean() 迁移失败主因是 STRICT_TRANS_TABLES 模式下 TINYINT(1) 不接受非 0/1 值,需先清洗脏数据、对齐字段符号性、确保 $casts 键名一致,并在 API 层预处理字符串布尔值。

为什么 boolean() 迁移在 MySQL 8.0.16+ 会出错
不是迁移写错了,而是 MySQL 默认关闭了 TINYINT(1) 的布尔隐式映射。Laravel 9.2+ 的 boolean() 生成的是 TINYINT(1) UNSIGNED NOT NULL,但如果你的数据库 sql_mode 含 STRICT_TRANS_TABLES(MySQL 8.0.16+ 默认开启),而旧数据里有 2、-1 这类值,ALTER TABLE ... MODIFY active TINYINT(1) UNSIGNED 就会直接报错:SQLSTATE[HY000]: General error: 1366 Incorrect integer value。
- 先查真实数据:运行
SELECT active, COUNT(*) FROM users GROUP BY active;,确认有没有非0/1值 - 别跳过清洗:有脏数据时,
change()必定失败,必须先执行UPDATE users SET active = CASE WHEN active != 0 THEN 1 ELSE 0 END; - 注意列定义是否带
SIGNED:如果原字段是TINYINT(1) SIGNED,而boolean()强制UNSIGNED,会冲突;要么加->unsigned()显式声明,要么先用tinyInteger()->signed()改类型再转
多数据库兼容时别依赖 TINYINT(1) 的布尔语义
PostgreSQL 根本没有 TINYINT,SQLite 的 BOOLEAN 是伪类型,底层存整数但 Laravel 解析逻辑不同——MySQL 走自定义解析器,SQLite 走 filter_var($value, FILTER_VALIDATE_BOOLEAN)。这意味着同一段 $casts = ['active' => 'boolean'] 在不同环境可能行为不一致。
- 跨库项目建议统一用
tinyInteger('active')->unsigned()->default(0),并在模型$casts中仍写'active' => 'boolean',靠 PHP 层转换兜底 - 避免在迁移里写
->boolean('active')->default(true)然后期望 PostgreSQL 自动建BOOLEAN字段——它会建BOOLEAN,但 Laravel 对它的读取逻辑和 MySQL 不同,容易在测试环境漏掉问题 - 如果真要强一致性,改用自定义 cast 类,显式控制各驱动下的 get/set 行为,而不是赌框架默认逻辑
boolean() 迁移后模型 $casts 必须对齐
迁移把字段改成 BOOLEAN(或 TINYINT(1) UNSIGNED)只是第一步。如果模型里 $casts 还是空的,或者键名拼错(比如数据库字段叫 is_active,模型写了 'active' => 'boolean'),那读出来永远是整数或字符串,不是布尔值。
- 确认键名完全一致:包括下划线、大小写,
is_active≠isActive≠active - 用
gettype($user->is_active)或var_export($user->is_active)验证真实类型,别信dd()输出的显示形式(true和int(1)看起来一样,但类型不同) - 优先用数组语法:
protected $casts = ['is_active' => 'boolean'];,别用旧版'bool',部分 Laravel 版本不识别
API 请求里布尔字段传字符串怎么办
前端发 {"is_active": "true"},模型 $casts 再准也没用——Eloquent 只在从数据库读取时做 cast,写入时不会自动把字符串转布尔。验证规则 'is_active' => 'boolean' 本身也不接受 "true" 字符串,会直接报 The is_active field must be true or false.。
- 在
FormRequest的prepareForValidation()里统一预处理:$this->merge(['is_active' => filter_var($this->is_active, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)]) - 别用
(bool) $value:因为(bool) "0"是true,业务上绝对反直觉 - 如果字段允许为空,验证规则必须加
nullable:'is_active' => 'nullable|boolean',否则null或空字符串也会挂掉
最麻烦的不是语法怎么写,而是脏数据 + 多环境 + 前端乱传这三者叠加。清洗数据、对齐迁移与模型、拦截请求体转换,三步缺一不可。漏掉任何一步,上线后都可能在某个环境突然崩掉。


















