How do I sort a Laravel Collection by multiple properties with both asc and desc?
Solution 1
Whoever uses this, just keep in mind - you'll need to tweak it depending if your using collection of objects or associative arrays. Should be an easy tweak. Just change the $a[]/$b[] stuff to $a-> and $b->
public static function multiPropertySort(Collection $collection, array $sorting_instructions){
return $collection->sort(function ($a, $b) use ($sorting_instructions){
//stuff starts here to answer question...
foreach($sorting_instructions as $sorting_instruction){
$a[$sorting_instruction['column']] = (isset($a[$sorting_instruction['column']])) ? $a[$sorting_instruction['column']] : '';
$b[$sorting_instruction['column']] = (isset($b[$sorting_instruction['column']])) ? $b[$sorting_instruction['column']] : '';
if(empty($sorting_instruction['order']) or strtolower($sorting_instruction['order']) == 'asc'){
$x = ($a[$sorting_instruction['column']] <=> $b[$sorting_instruction['column']]);
}else{
$x = ($b[$sorting_instruction['column']] <=> $a[$sorting_instruction['column']]);
}
if($x != 0){
return $x;
}
}
return 0;
})->values();
}
Solution 2
If you are using Eloquent to get your collection instance, It would be much better to use orderBy method in your query, especially if columns were indexed:
$sorting_insructions = [
['column'=>'first_name', 'order'=>'asc'],
['column'=>'date_of_birth', 'order'=>'desc'],
];
$collection = App\User::query();
foreach ($sorting_insructions as $value) {
$collection->orderBy($value['column'], $value['order']);
}
$users = $collection->get();
EDIT Since the question was edited telling that sort should be used outside the query builder, I think chaining sortBy and sortByDesc in reverse order from $sorting_insructions
gives the same result:
$collection = App\User::all();
$sorting_insructions = [
['column'=>'first_name', 'order'=>'asc'],
['column'=>'date_of_birth', 'order'=>'desc'],
];
for ($i = count($sorting_insructions) - 1; $i >= 0 ; $i--) {
extract($sorting_insructions[i]);
if ( $order === 'asc') {
$collection = $collection->sortBy( $column );
} else {
$collection = $collection->sortByDesc( $column );
}
}
Solution 3
I know this question is from a while back, but in case anyone stumbles across it, Laravel does provide this functionality now (v8.x at the time of writing).
See the official docs for more detail. (4th code block example in the sort-by section) https://laravel.com/docs/8.x/collections#method-sortby
From the docs:
If you would like to sort your collection by multiple attributes, you may pass an array of sort operations to the sortBy method. Each sort operation should be an array consisting of the attribute that you wish to sort by and the direction of the desired sort:
$collection = collect([
['name' => 'Taylor Otwell', 'age' => 34],
['name' => 'Abigail Otwell', 'age' => 30],
['name' => 'Taylor Otwell', 'age' => 36],
['name' => 'Abigail Otwell', 'age' => 32],
]);
$sorted = $collection->sortBy([
['name', 'asc'],
['age', 'desc'],
]);
$sorted->values()->all();
/*
[
['name' => 'Abigail Otwell', 'age' => 32],
['name' => 'Abigail Otwell', 'age' => 30],
['name' => 'Taylor Otwell', 'age' => 36],
['name' => 'Taylor Otwell', 'age' => 34],
]
*/
Applied to the example from the question:
/*
$sorting_instructions = [
['column'=>'first_name', 'order'=>'asc'],
['column'=>'date_of_birth', 'order'=>'desc'],
];
*/
$collection = User::all();
$sortedCollection = $collection->sortBy([
['first_name','asc'],
['date_of_birth','desc'],
])
Solution 4
Simple as doing this:
return $this->items->sortBy([
'is_hidden', 'asc',
'title', 'asc',
]);
:)
Tarek Adam
As a self-taught technologist working with well trained computer scientists and software engineers, I must follow the highest industry standards or be destroyed.
Updated on June 19, 2022Comments
-
Tarek Adam almost 2 years
If I have an Illuminate\Support\Collection, how do I sort by multiple properties with both asc and desc? (This is a simple hypothetical - not at all looking for tips on query building.)
$collection = User::all(); // not looking for User::orderBy()->get() solutions. Read the question. $sorting_insructions = [ ['column'=>'first_name', 'order'=>'asc'], ['column'=>'date_of_birth', 'order'=>'desc'], ]; $collection->sort(function($a,$b) use ($sorting_instructions){ // something... });