unsigned()仅为整数字段的链式修饰方法,不可独立使用;必须依附于tinyInteger()、integer()等类型后调用,如$table->integer('age')->unsigned(),或直接使用unsignedInteger()等封装语法糖。

unsigned() 方法只能链式调用,不能单独作为字段类型
很多人误以为 unsigned() 是一个独立的字段类型(比如像 bigInteger() 那样),结果写出 $table->unsigned('votes'); —— 这会直接报错:Call to undefined method Illuminate\Database\Schema\Blueprint::unsigned()。它只是整数类型字段的修饰方法,必须依附在 tinyInteger()、smallInteger()、integer()、bigInteger() 等后面使用。
正确写法是:
$table->tinyInteger('status')->unsigned();
或者更常见的是连写:
$table->unsignedTinyInteger('status');
后者本质是前者的语法糖,Laravel 内部已封装好,推荐优先使用带 unsigned 前缀的版本,语义更清晰,也避免漏掉链式调用。
unsignedBigInteger() 在外键场景下必须与 references 字段严格匹配
当用 unsignedBigInteger() 定义外键(比如 user_id)时,如果关联的主表 users.id 是默认的 bigIncrements()(即 unsignedBigInteger('id')->primary()->autoIncrement()),那没问题;但如果主表字段是手动定义的 bigInteger('id')->primary()(没加 unsigned()),就会在运行迁移时失败,报错:SQLSTATE[HY000]: General error: 1005 Can't create table ... errno: 150 "Foreign key constraint is incorrectly formed"。
关键点在于:MySQL 要求外键列和被引用列的类型、符号性(signed/unsigned)、长度必须完全一致。
- ✅ 正确配对:
unsignedBigInteger('user_id')→bigIncrements('id') - ❌ 错误配对:
unsignedBigInteger('user_id')→bigInteger('id')(后者默认 signed) - ⚠️ 注意:
increments()和bigIncrements()自动带unsigned,但integer()、bigInteger()默认不带
unsigned 整数字段的取值范围变化直接影响业务逻辑
加了 unsigned 后,同样位宽的整数能存更大的正数,但彻底失去负数能力。例如:
-
tinyInteger():-128 ~ 127 -
unsignedTinyInteger():0 ~ 255 -
integer():-2,147,483,648 ~ 2,147,483,647 -
unsignedInteger():0 ~ 4,294,967,295
这意味着如果你在已有数据的表上补加 unsigned(比如用 change()),而原字段含负值,MySQL 会静默截断为 0 或报错(取决于 SQL mode),导致数据损坏。所以:
- 新表设计时尽早决定是否需要负值,避免后期改造
- 不要对已有负值数据的字段执行
->unsigned()->change() - 若真要转,先查清当前最小值:
SELECT MIN(score) FROM results;,确认全 ≥ 0 再操作
SQLite 不支持 unsigned,开发环境需注意兼容性陷阱
Laravel 的 Schema 构建器在 SQLite 驱动下会忽略 unsigned() 调用——既不报错,也不生效。也就是说,你在 SQLite 上跑 unsignedInteger('amount'),实际建出来的还是普通有符号整型。这会导致:
- 本地开发时测试不到 unsigned 约束(比如意外插入 -100 不报错)
- 上线到 MySQL 后突然因越界或外键不匹配出问题
解决方案不是禁用 unsigned,而是明确开发环境尽量贴近生产:
- 本地也用 Docker 跑 MySQL,而非依赖 SQLite 默认配置
- CI 流程中强制用 MySQL 执行迁移测试
- 避免在模型验证里假设“unsigned 字段天然 > 0”,仍应加
min:0规则兜底
unsigned 字段本身不难写,难的是它把数据库约束、驱动差异、数据迁移、业务语义全串在了一起——少看一眼文档,就可能在线上半夜收到报警。











