Laravel 5.4多字段sortBy排序异常,求正确实现方法
解决Laravel 5.4集合多字段排序问题
你的问题出在链式调用多个sortBy/sortByDesc方法的逻辑上——这类方法并不是叠加排序(即在前序排序的基础上对相同值元素二次排序),而是每次调用都会对整个集合重新排序,再加上PHP的排序算法是不稳定的,导致相同排序条件的元素顺序无法保证符合预期。
正确的多字段排序实现
Laravel集合的sortBy(或sortByDesc)支持通过回调返回一个排序优先级数组,数组元素的顺序就是你的排序规则优先级,升序字段直接返回值,降序字段返回其负值(利用sortBy的升序逻辑实现降序效果)。
针对你的排序规则:score(升序)> int_premi(升序)> rates(降序)> promo(布尔值降序)> views(降序)> product_id(降序),可以这样写:
$sortedCollection = $collection->sortBy(function ($item) { return [ // 升序字段:直接返回字段值 $item->score, $item->int_premi, // 降序字段:返回负值,让sortBy升序等价于原字段降序 -$item->rates, // promo布尔降序:有promo的在前,用负值转换排序方向 -(empty($item->promo) ? 0 : 1), -$item->views, -$item->product_id, ]; })->values()->all();
代码解释
- 排序优先级数组:数组的第一个元素是最高优先级排序字段,依次往后降低,Laravel会自动按数组元素的顺序依次比较排序。
- 降序处理:对需要降序的字段(如
rates、views)取负值,这样sortBy的升序排序就等价于原字段的降序排序。 - promo布尔排序:用
empty($item->promo) ? 0 : 1标记有无promo,再取负值,让有promo的元素(对应-1)排在无promo的元素(对应0)前面,实现布尔值降序。
为什么你的原写法不对?
链式调用sortByDesc('product_id')->sortByDesc('views')...时,每个排序方法都会重新打乱整个集合的顺序,仅当当前排序字段值完全相同时,才会保留之前的元素顺序(但PHP的usort是不稳定排序,这一点也无法保证)。比如你先按product_id降序,再按promo排序时,相同promo的元素顺序可能会被打乱,无法维持之前的product_id排序结果。
使用单个sortBy回调返回排序数组的方式,能让Laravel严格按照你定义的优先级依次比较,完美实现多字段排序需求。
内容的提问来源于stack exchange,提问作者Its Wawan
相关产品推荐
相关产品推荐

