必须逐层核验mysql连接层、服务端、表结构、迁移文件、php环境五处utf8mb4配置是否全部到位,漏掉任意一环都可能让前几步白做:第一步设laravel连接层charset/collation及pdo初始化命令;第二步配mysql服务端character-set-server和collation-server并重启验证;第三步用convert to升级已有库表字符集;第四步在迁移中显式声明字段collation或执行alter table;第五步确保pdo版本≥5.6且redis等周边组件同步设utf8mb4。

避免Laravel存中文变问号、emoji报错SQLSTATE[HY000]: General error: 1366 Incorrect string value,必须逐层核验MySQL连接层、服务端、表结构、迁移文件、PHP环境五处utf8mb4配置是否全部到位,漏掉任意一环都可能让前几步白做。
第一步:确认 Laravel 连接层字符集已强制设为 utf8mb4
打开 config/database.php,找到 mysql 配置块,检查是否同时包含以下三项:
'charset' => 'utf8mb4' 和 'collation' => 'utf8mb4_unicode_ci' —— 这两项只控制表和列的默认值,【不设 PDO 初始化命令就等于没设】;
必须额外加上:'options' => [PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"];
别信 .env 里的 DB_CHARSET=utf8mb4 —— Laravel 官方代码根本没读这个变量,删掉免得误导自己。
第二步:验证 MySQL 服务端已启用 utf8mb4 全局支持
编辑 MySQL 配置文件(Linux 是 /etc/my.cnf,Windows 是 my.ini),在 [mysqld] 段落中确认存在:
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
改完必须重启 MySQL 服务;
重启后进 MySQL 命令行执行:SHOW VARIABLES LIKE 'character_set_server'; 和 SHOW VARIABLES LIKE 'collation_server';,两行结果都必须是 utf8mb4 和 utf8mb4_unicode_ci;
如果其中任一不是,说明配置未生效或未重启,迁移和写入仍会走默认 utf8。
第三步:已有数据库和表必须手动升级字符集
哪怕连接层和服务端都对了,旧库旧表还是 utf8 编码,字段里存的中文或 emoji 依然会乱码;
先建库时就该用:CREATE DATABASE your_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;;
对已有库中的每张表执行:ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;;
注意:只改表默认字符集(如用 ALTER TABLE users DEFAULT CHARACTER SET utf8mb4)【不会更新已有字段】,必须用 CONVERT TO 才能真正重写所有列。
第四步:Laravel 迁移文件必须显式覆盖默认建表行为
Laravel 9.x 之前,Schema::create() 默认用 utf8 建表,哪怕你改了 config/database.php 也不生效;
方法一:在迁移文件末尾追加原生语句:DB::statement("ALTER TABLE posts CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");;
方法二(推荐):在 AppServiceProvider::boot() 中加 Schema::defaultStringLength(191);,再配合字段级声明:$table->string('title')->charset('utf8mb4');;
方法三:直接在迁移里为每个字段指定排序规则,例如:$table->string('name')->collation('utf8mb4_unicode_ci'); —— 注意整数类型不支持 collation(),别往 integer() 后面链式调用。
第五步:验证 PHP 环境与周边组件编码一致性
PHP 本身无需改编码,但需确保 PDO 扩展支持 utf8mb4 —— 查看 phpinfo() 中 PDO drivers 是否含 mysql 且版本 ≥ 5.6;
Redis 若用于缓存模型,其连接也需设 charset => utf8mb4,否则取出来的中文字段可能被 Redis 自动转义;
最后跑一次迁移:php artisan migrate:fresh --seed,然后插入一条带 emoji 的记录测试:DB::table('users')->insert(['name' => '张三??']);,查出来仍是原样才算真正闭环。











