Yii中用UUID作主键需重写primaryKey()返回['id']字符串数组,UuidBehavior的value必须用匿名函数延迟生成,数据库字段应为VARCHAR(36)或CHAR(36),关联查询时须确保主外键类型一致。

Yii模型里用UUID当主键,primaryKey()必须返回字符串数组
Yii的AR模型默认假设主键是整数型,一旦用UUID做主键,primaryKey()方法返回值类型不对,会导致save()失败、findOne()查不到、关联查询出错。核心不是“能不能用”,而是“怎么告诉框架它真是主键”。
正确做法是重写模型的primaryKey()方法,明确返回一个含单个字段名的数组:
public static function primaryKey()
{
return ['id']; // 注意:这里必须是字符串字段名,不是值,也不是UUID生成逻辑
}
如果返回['id' => 'string']或null,框架会误判主键结构,后续所有AR操作都可能静默失效。
UuidBehavior要配在behaviors()里,且value不能直接调用Uuid::uuid4()
Yii2的UuidBehavior(来自yii2-uuid扩展)不会自动填充字段,它依赖value配置项提供生成逻辑。常见错误是写成Uuid::uuid4()->toString()——这会在类加载时就执行一次,导致所有记录主键相同。
必须用匿名函数延迟执行:
- ✅ 正确:
'value' => function () { return \Ramsey\Uuid\Uuid::uuid4()->toString(); } - ❌ 错误:
'value' => \Ramsey\Uuid\Uuid::uuid4()->toString()(立即求值) - ⚠️ 注意:
UuidBehavior默认只在INSERT时触发,更新时不覆盖;如需更新也生成新UUID,得加'skipUpdate' => false
数据库字段类型必须是VARCHAR(36)或CHAR(36),不能用BINARY(16)除非手动转换
虽然BINARY(16)存UUID更省空间,但Yii默认不处理二进制UUID和字符串UUID之间的自动转换。一旦数据库字段是BINARY(16),而UuidBehavior输出的是字符串格式(如550e8400-e29b-41d4-a716-446655440000),就会报SQLSTATE[HY000]: General error: 1366 Incorrect string value这类错误。
最省事的方案就是统一用字符串存储:
- MySQL建表:
id VARCHAR(36) NOT NULL PRIMARY KEY - PostgreSQL:
id CHAR(36) PRIMARY KEY或直接用UUID类型(但需Behavior适配) - 别在迁移里写
$this->binary(16)然后指望框架自动编解码——它不会
关联查询时joinWith()和with()能用,但要注意ON条件里的类型隐式转换
UUID主键模型做关联时,比如User hasMany Order,如果Order.user_id也是UUID字符串,那with('user')完全没问题。但若一方是整数ID、另一方是UUID,MySQL可能因类型不匹配走全表扫描,甚至返回空结果而不报错。
排查要点:
- 检查关联定义中
link数组的字段名是否拼写一致(大小写敏感) - 确认外键字段类型和主键字段类型完全一致(都是
VARCHAR(36)) - 用
createCommand()->sql打印最终SQL,看ON user.id = order.user_id两边是否都被当字符串处理——如有CAST或CONVERT说明隐式转换已发生,性能堪忧
UUID做主键本身不难,难的是从数据库字段定义、Behavior配置、AR元数据声明到关联逻辑,每层都得对齐“它是个字符串主键”这个事实。漏掉任意一环,问题都藏得深,表现还五花八门。










