diff --git a/src/Illuminate/Collections/Arr.php b/src/Illuminate/Collections/Arr.php index be75b1dad85e..9d08ae2c8c24 100644 --- a/src/Illuminate/Collections/Arr.php +++ b/src/Illuminate/Collections/Arr.php @@ -604,7 +604,7 @@ public static function shuffle($array, $seed = null) * Sort the array using the given callback or "dot" notation. * * @param array $array - * @param callable|string|null $callback + * @param callable|array|string|null $callback * @return array */ public static function sort($array, $callback = null) diff --git a/src/Illuminate/Collections/Collection.php b/src/Illuminate/Collections/Collection.php index 15d17ce441fe..6a9e96f4ed54 100644 --- a/src/Illuminate/Collections/Collection.php +++ b/src/Illuminate/Collections/Collection.php @@ -1110,13 +1110,17 @@ public function sortDesc($options = SORT_REGULAR) /** * Sort the collection using the given callback. * - * @param callable|string $callback + * @param callable|array|string $callback * @param int $options * @param bool $descending * @return static */ public function sortBy($callback, $options = SORT_REGULAR, $descending = false) { + if (is_array($callback)) { + return $this->sortByMany($callback); + } + $results = []; $callback = $this->valueRetriever($callback); @@ -1141,6 +1145,50 @@ public function sortBy($callback, $options = SORT_REGULAR, $descending = false) return new static($results); } + /** + * Sort the collection using multiple comparisons. + * + * @param array $comparisons + * @return static + */ + protected function sortByMany(array $comparisons = []) + { + $items = $this->items; + + usort($items, function ($a, $b) use ($comparisons) { + foreach ($comparisons as $comparison) { + $comparison = Arr::wrap($comparison); + + $prop = $comparison[0]; + + $ascending = Arr::get($comparison, 1, true) === true || + Arr::get($comparison, 1, true) === 'asc'; + + $result = 0; + + if (is_callable($prop)) { + $result = $prop($a, $b); + } else { + $values = [Arr::get($a, $prop), Arr::get($b, $prop)]; + + if (! $ascending) { + $values = array_reverse($values); + } + + $result = $values[0] <=> $values[1]; + } + + if ($result === 0) { + continue; + } + + return $result; + } + }); + + return new static($items); + } + /** * Sort the collection in descending order using the given callback. * diff --git a/tests/Support/SupportArrTest.php b/tests/Support/SupportArrTest.php index 1b4a280ca5a0..7356c4378d03 100644 --- a/tests/Support/SupportArrTest.php +++ b/tests/Support/SupportArrTest.php @@ -903,4 +903,57 @@ public function testWrap() $this->assertEquals([$obj], Arr::wrap($obj)); $this->assertSame($obj, Arr::wrap($obj)[0]); } + + public function testSortByMany() + { + $unsorted = [ + ['name' => 'John', 'age' => 8, 'meta' => ['key' => 3]], + ['name' => 'John', 'age' => 10, 'meta' => ['key' => 5]], + ['name' => 'Dave', 'age' => 10, 'meta' => ['key' => 3]], + ['name' => 'John', 'age' => 8, 'meta' => ['key' => 2]], + ]; + + // sort using keys + $sorted = array_values(Arr::sort($unsorted, [ + 'name', + 'age', + 'meta.key', + ])); + $this->assertEquals([ + ['name' => 'Dave', 'age' => 10, 'meta' => ['key' => 3]], + ['name' => 'John', 'age' => 8, 'meta' => ['key' => 2]], + ['name' => 'John', 'age' => 8, 'meta' => ['key' => 3]], + ['name' => 'John', 'age' => 10, 'meta' => ['key' => 5]], + ], $sorted); + + // sort with order + $sortedWithOrder = array_values(Arr::sort($unsorted, [ + 'name', + ['age', false], + ['meta.key', true], + ])); + $this->assertEquals([ + ['name' => 'Dave', 'age' => 10, 'meta' => ['key' => 3]], + ['name' => 'John', 'age' => 10, 'meta' => ['key' => 5]], + ['name' => 'John', 'age' => 8, 'meta' => ['key' => 2]], + ['name' => 'John', 'age' => 8, 'meta' => ['key' => 3]], + ], $sortedWithOrder); + + // sort using callable + $sortedWithCallable = array_values(Arr::sort($unsorted, [ + function ($a, $b) { + return $a['name'] <=> $b['name']; + }, + function ($a, $b) { + return $b['age'] <=> $a['age']; + }, + ['meta.key', true], + ])); + $this->assertEquals([ + ['name' => 'Dave', 'age' => 10, 'meta' => ['key' => 3]], + ['name' => 'John', 'age' => 10, 'meta' => ['key' => 5]], + ['name' => 'John', 'age' => 8, 'meta' => ['key' => 2]], + ['name' => 'John', 'age' => 8, 'meta' => ['key' => 3]], + ], $sortedWithCallable); + } }