php 按多列对Eloquent集合进行排序的语法是什么?

ktca8awb  于 2023-06-04  发布在  PHP
关注(0)|答案(4)|浏览(186)

我知道在使用查询生成器时,可以使用

...orderBy('column1')->orderBy('column2')

但现在我处理的是集合对象。集合有sortBy方法,但我还没有弄清楚如何使它适用于多个列。直观地说,我最初尝试使用与orderBy相同的语法。

sortBy('column1')->sortBy('column2)

但这显然只是顺序地应用排序,并且它最终按column2排序,而忽略column1。我尽力了

sortBy('column1', 'column2')

但是这会抛出错误“asort()expects parameter 2 to be long,string given”。使用

sortBy('column1, column2')

不会抛出错误,但是排序看起来很随机,所以我真的不知道它到底做了什么。我查看了sortBy方法的代码,但不幸的是,我很难理解它是如何工作的。

hfsqlsce

hfsqlsce1#

sortBy()采用闭包,允许您提供用于排序比较的单个值,但您可以通过将多个属性连接在一起使其成为一个复合值

$posts = $posts->sortBy(function($post) {
    return sprintf('%-12s%s', $post->column1, $post->column2);
});

如果您需要对多个列使用sortBy,您可能需要对它们进行空格填充,以确保“ABC”和“DEF”在“AB”和“DEF”之后,因此sprint对每一列进行填充,直到该列的长度(至少对除最后一列之外的所有列)
请注意,如果可以在查询中使用orderBy,那么从数据库中检索集合时集合已经排序好,那么效率通常会高得多

yuvru6vn

yuvru6vn2#

我发现了一种不同的方法,在雄辩的集合上使用sort()。它可能比填充字段更好一点,或者至少更容易理解。这一个有更多的比较,但我不做sprintf()的每一个项目。

$items = $items->sort(
    function ($a, $b) {
        // sort by column1 first, then 2, and so on
        return strcmp($a->column1, $b->column1)
            ?: strcmp($a->column2, $b->column2)
            ?: strcmp($a->column3, $b->column3);
    }
);
von4xj4u

von4xj4u3#

正如@derekaug提到的,sort方法允许我们输入一个自定义闭包来对集合进行排序。但我认为他的解决方案写起来 * 有点麻烦 *,如果有这样的东西就好了:

$collection = collect([/* items */])
$sort = ["column1" => "asc", "column2" => "desc"];
$comparer = $makeComparer($sort);
$collection->sort($comparer);

事实上,这可以通过以下$makeComparer Package 器轻松存档,以生成compare闭包:

$makeComparer = function($criteria) {
  $comparer = function ($first, $second) use ($criteria) {
    foreach ($criteria as $key => $orderType) {
      // normalize sort direction
      $orderType = strtolower($orderType);
      if ($first[$key] < $second[$key]) {
        return $orderType === "asc" ? -1 : 1;
      } else if ($first[$key] > $second[$key]) {
        return $orderType === "asc" ? 1 : -1;
      }
    }
    // all elements were equal
    return 0;
  };
  return $comparer;
};

示例

$collection = collect([
  ["id" => 1, "name" => "Pascal", "age" => "15"],
  ["id" => 5, "name" => "Mark", "age" => "25"],
  ["id" => 3, "name" => "Hugo", "age" => "55"],
  ["id" => 2, "name" => "Angus", "age" => "25"]
]);

$criteria = ["age" => "desc", "id" => "desc"];
$comparer = $makeComparer($criteria);
$sorted = $collection->sort($comparer);
$actual = $sorted->values()->toArray();

/**
* [
*  ["id" => 5, "name" => "Hugo", "age" => "55"],
*  ["id" => 3, "name" => "Mark", "age" => "25"],
*  ["id" => 2, "name" => "Angus", "age" => "25"],
*  ["id" => 1, "name" => "Pascal", "age" => "15"],
* ];
*/

$criteria = ["age" => "desc", "id" => "asc"];
$comparer = $makeComparer($criteria);
$sorted = $collection->sort($comparer);
$actual = $sorted->values()->toArray();

/**
* [
*  ["id" => 5, "name" => "Hugo", "age" => "55"],
*  ["id" => 2, "name" => "Angus", "age" => "25"],
*  ["id" => 3, "name" => "Mark", "age" => "25"],
*  ["id" => 1, "name" => "Pascal", "age" => "15"],
* ];
*/

$criteria = ["id" => "asc"];
$comparer = $makeComparer($criteria);
$sorted = $collection->sort($comparer);
$actual = $sorted->values()->toArray();

/**
* [
*  ["id" => 1, "name" => "Pascal", "age" => "15"],
*  ["id" => 2, "name" => "Angus", "age" => "25"],
*  ["id" => 3, "name" => "Mark", "age" => "25"],
*  ["id" => 5, "name" => "Hugo", "age" => "55"],
* ];
*/

现在,既然我们在这里谈论Eloquent,那么你也在使用Laravel的可能性很高。因此,我们甚至可以将$makeComparer()闭包绑定到IOC并从那里解析它:

// app/Providers/AppServiceProvider.php 
// in Laravel 5.1
class AppServiceProvider extends ServiceProvider
{
    /**
     * ...
     */

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind("collection.multiSort", function ($app, $criteria){
                return function ($first, $second) use ($criteria) {
                    foreach ($criteria as $key => $orderType) {
                        // normalize sort direction
                        $orderType = strtolower($orderType);
                        if ($first[$key] < $second[$key]) {
                            return $orderType === "asc" ? -1 : 1;
                        } else if ($first[$key] > $second[$key]) {
                            return $orderType === "asc" ? 1 : -1;
                        }
                    }
                    // all elements were equal
                    return 0;
                };
        });
    }
}

现在你可以在任何你需要的地方使用它:

$criteria = ["id" => "asc"];
$comparer = $this->app->make("collection.multiSort",$criteria);
$sorted = $collection->sort($comparer);
$actual = $sorted->values()->toArray();
9q78igpj

9q78igpj4#

一个简单的解决方案是以与您希望的排序方式相反的顺序多次链接sortBy()。缺点是,这可能比在同一个回调中一次排序要慢,所以在大型集合中使用风险自负。

$collection->sortBy('column3')->sortBy('column2')->sortBy('column1');

相关问题