What is the syntax for sorting an Eloquent collection by multiple columns?

前端 未结 4 1955

I know that when using the query builder, it is possible to sort by multiple columns using

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

4条回答
  •  甜味超标
    2020-12-01 05:35

    As @derekaug mentioned, the sort method allows us to enter a custom closure for sorting the collection. But I thought his solution was somewhat cumbersome to write and it woulde be nice to have something like this:

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

    In fact, this can be easily archived by the following $makeComparer wrapper to generate the compare closure:

    $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;
    };
    

    Examples

    $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"],
    * ];
    */
    

    Now, since we're talking Eloquent here, chances are high that you're also using Laravel. So we might even bind the $makeComparer() closure to the IOC and resolve it from there:

    // 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;
                    };
            });
        }
    }
    

    Now you can use it everywhere you need to like so:

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

提交回复
热议问题