Laravel中基于多态关联排序数据库结果的问题
解决Laravel多态关联下按Meta字段排序的问题
首先先确认你的模型关联是否正确定义,这是后续操作的基础:
模型关联定义
在User模型中添加多态关联:
public function metas() { return $this->morphMany(Meta::class, 'owner'); }
在Meta模型中定义反向关联:
public function owner() { return $this->morphTo(); }
接下来针对你需要按meta表中key="test"的value排序的需求,这里提供两种实用的解决方案,分别适用于不同场景:
方案一:使用Join语句(推荐,性能更优)
直接通过leftJoin关联meta表,筛选出目标key的记录,同时处理字符串转数字的排序问题(因为你的value是string类型但存的是数字,直接排序会出现"10"排在"2"前面的坑):
$users = User::select('users.*') ->leftJoin('meta', function ($join) { $join->on('users.id', '=', 'meta.owner_id') ->where('meta.owner_type', '=', User::class) // 替换成你的User模型完整类名 ->where('meta.key', '=', 'test'); }) // 将string类型的value转为无符号整数,保证数字排序逻辑正确 ->orderByRaw('CAST(meta.value AS UNSIGNED) ASC') ->get();
注意事项:
- 用
leftJoin可以保留那些没有test这条meta记录的用户;如果只需要包含有该meta的用户,换成innerJoin即可。 - 如果需要降序排序,把
ASC改成DESC。 - 如果你的
value字段可能存在非数字内容,可以用CASE语句做容错处理,比如:->orderByRaw('CASE WHEN meta.value REGEXP "^[0-9]+$" THEN CAST(meta.value AS UNSIGNED) ELSE 999999 END ASC')
方案二:使用子查询排序
如果你不想用join,也可以通过子查询的方式实现排序,这种方式更直观但性能略逊于join:
$users = User::orderByRaw( 'COALESCE((SELECT value FROM meta WHERE owner_id = users.id AND owner_type = ? AND key = ?), 0) ASC', [User::class, 'test'] )->get();
这里用COALESCE给没有testmeta的用户设置默认排序值(这里设为0,你可以根据需求调整),确保排序结果符合预期。
额外场景:筛选并排序
如果需要先筛选出拥有testmeta的用户,再排序,可以结合whereHas和join:
$users = User::whereHas('metas', function ($query) { $query->where('key', 'test'); }) ->join('meta', function ($join) { $join->on('users.id', '=', 'meta.owner_id') ->where('meta.owner_type', '=', User::class) ->where('meta.key', '=', 'test'); }) ->orderByRaw('CAST(meta.value AS UNSIGNED) DESC') ->select('users.*') // 避免字段冲突 ->get();
内容的提问来源于stack exchange,提问作者Zoha
相关产品推荐
相关产品推荐

