How do I sort a Laravel Collection by multiple properties with both asc and desc?

17,688

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',
]);

:)

Share:
17,688
Tarek Adam
Author by

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, 2022

Comments

  • Tarek Adam
    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...
    });