Hyperf 3.1 ORM 中关联字段“丢失”实为未显式选取且缺表前缀,需用 join() 显式关联并在 select() 中明确写出带别名的字段(如 'profiles.avatar as profile_avatar'),避免 with()、select('*') 及 LEFT JOIN 条件误写在 where() 中。

Hyperf 3.1 中 ORM 使用 select() 指定字段后,关联表字段“丢失”,本质不是真的丢失,而是字段未被显式选取 + 别名/前缀缺失导致结果中不可见或被覆盖。
必须显式写出关联表字段,并加表别名前缀
Hyperf 的 QueryBuilder(包括 ORM 的查询构造)不会自动把关联表字段塞进结果。即使你写了 with('profile'),select('users.id', 'users.name') 也不会自动带上 profiles.avatar。
- 正确写法:在
select()中明确列出所有需要的字段,且跨表字段必须带别名或表前缀,例如'profiles.avatar as profile_avatar'或'p.avatar'(若 join 时用了别名->join('profiles as p', ...)) - 错误写法:
select('id', 'name', 'avatar')——avatar无表前缀,解析时无法定位,可能被忽略或报错
关联需用 join() 显式拼接,不能只靠 with()
with() 是 N+1 预加载机制,它在主查询之后发额外 SQL,不参与当前 select 字段组装。要让关联字段出现在同一结果行里,必须用 join() 手动关联。
- 例如查用户及资料头像:
->from('users')->join('profiles', 'profiles.user_id', '=', 'users.id')->select('users.id', 'users.name', 'profiles.avatar') - 如果仍想用模型关系语法,可结合
toBase()获取底层 QueryBuilder,再链式 join + select
LEFT JOIN 过滤条件必须写在 on() 里
若用 leftJoin() 查用户和可选资料,但又在 where() 中写了 profiles.status = 1,会导致 LEFT JOIN 实际退化为 INNER JOIN,看似“字段丢失”,实则是整行被过滤掉了。
- 正确:把关联表过滤条件放进
on(),如->leftJoin('profiles', function ($join) { $join->on('profiles.user_id', '=', 'users.id')->on('profiles.status', '=', 1); }) - 这样即使某用户无匹配资料,主表数据仍在,
profiles.avatar为null,而非整行消失
避免 select('*') 和字段名冲突
多个表含同名字段(如 id、created_at)时,select('*') 会导致后出现的字段覆盖前面的,PHP 数组键重复,只保留最后一个。
- 务必禁用
select('*'),改用白名单方式明确指定每个字段 - 对同名字段强制重命名,如
'users.id as user_id'、'orders.id as order_id' - Hyperf 不支持
select(['users.*', 'profiles.avatar'])这类混合写法,会报错或行为异常


















