Laravel按名称升序排序时优先展示Swiva开头店铺的实现方法
需求说明
开发店铺列表查询API时需要实现如下排序规则:
- 列表头部优先展示所有名称以
Swiva开头的店铺(如Swiva Electronics、Swiva Gas、Swiva Mall等) - 剩余店铺统一按照名称升序排列
原有代码问题
当前实现代码运行效果不符合预期,代码如下:
$shops=Shop::join('users','users.id','=','shops.owner_id') ->select('shops.id','shops.name','users.email','users.tel','users.estate','shops.logo') ->orderBy('shops.name') ->get() ->partition(function ($item) { return $item->name != 'Swiva Electronics'; })->flatten();
存在两个明确问题:
- 匹配到的目标店铺未排在列表头部,反而出现在列表末尾
- 判断逻辑仅能精确匹配
Swiva Electronics单个店铺,无法覆盖所有Swiva前缀的店铺
查询结果最终将通过如下JSON结构返回:
if(!$shops->isEmpty()){ return response()->json([ 'success'=>true, 'shops'=> $shops ]); }else if($shops->isEmpty()){ return response()->json([ 'success'=>false, 'message'=> 'There exists no subscribed shops from ' ]); }
实现方案
提供两种可直接落地的实现方式,按需选择即可:
方案1:数据库层排序(推荐,性能更好)
直接在Eloquent查询阶段通过原生排序规则实现需求,无需查询后遍历处理集合,数据量较大时性能优势明显:
$shops = Shop::join('users','users.id','=','shops.owner_id') ->select('shops.id','shops.name','users.email','users.tel','users.estate','shops.logo') // 自定义第一排序规则:Swiva开头的店铺权重为0,排最前 ->orderByRaw('CASE WHEN shops.name LIKE ? THEN 0 ELSE 1 END', ['Swiva%']) // 第二排序规则:所有店铺同组内按名称升序排列 ->orderBy('shops.name', 'asc') ->get();
注:MySQL环境下上述代码可直接运行,如果使用PostgreSQL等其他数据库,可将前缀判断逻辑替换为对应数据库支持的前缀匹配函数即可。
方案2:集合层处理(无需改动原有查询结构)
如果不想调整原有查询逻辑,可修正集合partition方法的判断规则和分组逻辑。原代码排序颠倒的核心原因是:partition方法会将满足闭包判断条件的元素放入返回的第一个集合,不满足的放入第二个集合,原判断条件写反了分组归属,才导致目标店铺排到末尾。修正后代码如下:
$shops = Shop::join('users','users.id','=','shops.owner_id') ->select('shops.id','shops.name','users.email','users.tel','users.estate','shops.logo') ->orderBy('shops.name') ->get() ->partition(function ($item) { // PHP 8.0+ 可直接用str_starts_with,更低版本替换为 strpos($item->name, 'Swiva') === 0 return str_starts_with($item->name, 'Swiva'); }) ->flatten();
两种方案生成的$shops结果完全符合排序要求,可直接代入原有JSON返回逻辑使用。
内容的提问来源于stack exchange,提问作者The Only Smart Boy
相关产品推荐
相关产品推荐

