Laravel中支持哪些迁移字段类型?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1157个文字,预计阅读时间需要5分钟。
Laravel 迁移中,避免使用万能字段,如 string、boolean、json、dateTime 等,这些类型覆盖 90% 场景。错误类型会导致查询失效、Eloquent 转换异常或跨库迁移失败。
boolean 字段不是存 0/1 的整数
很多人用 tinyInteger('is_active') 模拟开关,但这是错误的起点。Laravel 的 boolean('is_active') 在 MySQL 中生成 TINYINT(1),在 PostgreSQL 中生成 BOOLEAN,Eloquent 读取时自动转为 PHP true/false;而手动用 tinyInteger 存 0/1,Eloquent 不会做语义转换,$user->is_active === 0(int)而非 false(bool),后续 if ($user->is_active) 逻辑可能意外通过。
必须注意:
-
boolean字段默认不允许 null,如需三态(启用/禁用/未设置),得显式加->nullable() - MySQL 5.7.2+ 支持原生 BOOLEAN,旧版会退化为 TINYINT,但 Laravel 层行为一致,无需额外适配
- 不要用
enum存开关状态——它把业务逻辑硬编码进数据库,改一个值要重跑迁移
JSON 字段不能靠 $casts 就万事大吉
json('settings') 是存储用户偏好、表单配置等非结构化数据的首选,但光在模型里写 'settings' => 'array' 远不够。Eloquent 确实会自动序列化/反序列化,但局部更新(比如只改 settings.theme)不会触发完整重写,必须用数据库原生函数。
常见踩坑点:
- 用
$user->settings['theme'] = 'dark'; $user->save();看似正常,实际是全量覆盖整个 JSON 字段,其他键值可能被意外清空(尤其并发写入时) - MySQL 中局部更新得用
DB::raw("JSON_SET(settings, '$.theme', 'dark')"),PostgreSQL 则用jsonb_set(),语法不通用 -
where('settings->theme', 'dark')查询在 SQLite 上不生效——它没 JSON 提取函数,会静默返回空结果
日期时间字段要小心精度和时区
dateTime('published_at') 和 dateTimeTz('sent_at') 看似只差个 Tz,但底层差异极大:dateTime 存的是无时区时间戳(如 2026-04-24 15:30:00),数据库按服务器本地时区解释;dateTimeTz 则强制带时区信息(如 2026-04-24 15:30:00+08:00),PostgreSQL 原生支持,MySQL 8.0+ 才支持,SQLite 完全不支持。
实际影响:
- 跨时区部署时,用
dateTime+ 应用层统一 UTC 存储最稳妥;强行用dateTimeTz可能导致 MySQL 报错或降级为普通datetime - 需要毫秒精度(比如日志追踪)?得显式指定
->useMicroseconds(),否则默认秒级,2026-04-24 15:30:00.123会被截断成2026-04-24 15:30:00 -
timestamp类型在 Laravel 迁移中不推荐直接使用——它受 MySQLexplicit_defaults_for_timestamp配置影响,行为不稳定
外键字段必须匹配被引用列的类型和符号
定义外键时,foreignId('user_id') 看起来方便,但它默认对应 unsignedBigInteger,如果被引用的 users.id 是 increments()(即 unsignedInteger),就会类型不匹配,MySQL 报错 Cannot add or update a child row: a foreign key constraint fails。
安全做法:
- 主键用
id()(等价于bigIncrements)→ 外键统一用foreignId - 主键用
increments→ 外键必须用unsignedInteger('user_id'),再手动->foreign('user_id')->references('id')->on('users') - 千万别混用
integer和bigInteger做关联,哪怕数值看起来能装下,数据库层面类型不一致就拒绝建约束
字段类型不是填空题,是设计决策。一个 json 字段写着写着变成嵌套三层对象,一个 string 字段从用户名膨胀到富文本 HTML,这些都不是类型本身的问题,而是当初没想清楚「这个字段未来会不会被 WHERE 条件查」「会不会被 ORDER BY 排序」「要不要被前端直接渲染」——类型选得对,后面少一半调试时间。
本文共计1157个文字,预计阅读时间需要5分钟。
Laravel 迁移中,避免使用万能字段,如 string、boolean、json、dateTime 等,这些类型覆盖 90% 场景。错误类型会导致查询失效、Eloquent 转换异常或跨库迁移失败。
boolean 字段不是存 0/1 的整数
很多人用 tinyInteger('is_active') 模拟开关,但这是错误的起点。Laravel 的 boolean('is_active') 在 MySQL 中生成 TINYINT(1),在 PostgreSQL 中生成 BOOLEAN,Eloquent 读取时自动转为 PHP true/false;而手动用 tinyInteger 存 0/1,Eloquent 不会做语义转换,$user->is_active === 0(int)而非 false(bool),后续 if ($user->is_active) 逻辑可能意外通过。
必须注意:
-
boolean字段默认不允许 null,如需三态(启用/禁用/未设置),得显式加->nullable() - MySQL 5.7.2+ 支持原生 BOOLEAN,旧版会退化为 TINYINT,但 Laravel 层行为一致,无需额外适配
- 不要用
enum存开关状态——它把业务逻辑硬编码进数据库,改一个值要重跑迁移
JSON 字段不能靠 $casts 就万事大吉
json('settings') 是存储用户偏好、表单配置等非结构化数据的首选,但光在模型里写 'settings' => 'array' 远不够。Eloquent 确实会自动序列化/反序列化,但局部更新(比如只改 settings.theme)不会触发完整重写,必须用数据库原生函数。
常见踩坑点:
- 用
$user->settings['theme'] = 'dark'; $user->save();看似正常,实际是全量覆盖整个 JSON 字段,其他键值可能被意外清空(尤其并发写入时) - MySQL 中局部更新得用
DB::raw("JSON_SET(settings, '$.theme', 'dark')"),PostgreSQL 则用jsonb_set(),语法不通用 -
where('settings->theme', 'dark')查询在 SQLite 上不生效——它没 JSON 提取函数,会静默返回空结果
日期时间字段要小心精度和时区
dateTime('published_at') 和 dateTimeTz('sent_at') 看似只差个 Tz,但底层差异极大:dateTime 存的是无时区时间戳(如 2026-04-24 15:30:00),数据库按服务器本地时区解释;dateTimeTz 则强制带时区信息(如 2026-04-24 15:30:00+08:00),PostgreSQL 原生支持,MySQL 8.0+ 才支持,SQLite 完全不支持。
实际影响:
- 跨时区部署时,用
dateTime+ 应用层统一 UTC 存储最稳妥;强行用dateTimeTz可能导致 MySQL 报错或降级为普通datetime - 需要毫秒精度(比如日志追踪)?得显式指定
->useMicroseconds(),否则默认秒级,2026-04-24 15:30:00.123会被截断成2026-04-24 15:30:00 -
timestamp类型在 Laravel 迁移中不推荐直接使用——它受 MySQLexplicit_defaults_for_timestamp配置影响,行为不稳定
外键字段必须匹配被引用列的类型和符号
定义外键时,foreignId('user_id') 看起来方便,但它默认对应 unsignedBigInteger,如果被引用的 users.id 是 increments()(即 unsignedInteger),就会类型不匹配,MySQL 报错 Cannot add or update a child row: a foreign key constraint fails。
安全做法:
- 主键用
id()(等价于bigIncrements)→ 外键统一用foreignId - 主键用
increments→ 外键必须用unsignedInteger('user_id'),再手动->foreign('user_id')->references('id')->on('users') - 千万别混用
integer和bigInteger做关联,哪怕数值看起来能装下,数据库层面类型不一致就拒绝建约束
字段类型不是填空题,是设计决策。一个 json 字段写着写着变成嵌套三层对象,一个 string 字段从用户名膨胀到富文本 HTML,这些都不是类型本身的问题,而是当初没想清楚「这个字段未来会不会被 WHERE 条件查」「会不会被 ORDER BY 排序」「要不要被前端直接渲染」——类型选得对,后面少一半调试时间。

