Home » Php » How to sort Illuminate Collection by multiple columns in Laravel 5.1?

How to sort Illuminate Collection by multiple columns in Laravel 5.1?

Posted by: admin November 29, 2017 Leave a comment

Questions:

I have a an instance of Laravel’s Illuminate Collection. The array contains multiple properties.

I need to be able to sort the collection based on 2 different attributes.

I want to first sort by the attribute that is called “sort” and then by an attribute called “title”.

Additionally, I have another collection that I like to sort it by the column “sort” if the value of sort is not null, then shuffle the items that have null for “sort” value.

How can I do this type of sort?

Answers:

You can provide a callback function to Collection::sort:

$collection->sort(function($a, $b) {
   if($a->sort === $b->sort) {
     if($a->title === $b->title) {
       return 0;
     }
     return $a->title < $b->title ? -1 : 1;
   } 
   return $a->sort < $b->sort ? -1 : 1;
});

This is documented here.

Questions:
Answers:

If you’re using PHP 7, you can use the spaceship operator:

$collection->sort(function ($a, $b) {
    return $a->sort === $b->sort ? $a->title <=> $b->title : $a->sort <=> $b->sort;
});

The <=> symbol is called the spaceship operator (or technically: the combined comparison operator). You can read more about it in the RFC.

Questions:
Answers:

The other answers (and PHP’s documentation) were very helpful. After some experimenting, I found you can also sort on related tables as well.

Using the ->with($relatedTableName) on the collection, and then using the ->sort() as described, you can sort on a related table field by using:

$baseTableField = "tableField"; // Field name from primary table/model
$relatedTableMapString = "tableName.tableField"; // Field name from a related table

$collection->with('relatedTableName')->sort(function($a, $b) use ($relatedTableMapString, $baseTableField) {

    $relatedTableName = explode(".", $relatedTableMapString)[0];
    $relatedTableFieldName = explode(".", $relatedTableMapString)[1];

    if($a->$relatedTableName->$relatedTableFieldName === $b->$relatedTableName->$relatedTableFieldName) {
        if($a->$baseTableField === $b->$baseTableField) {
            return 0;
        }
        return $a->$baseTableField < $b->$baseTableField ? -1 : 1;
    }
    return $a->$relatedTableName->$relatedTableFieldName < $b->$relatedTableName->$relatedTableFieldName ? -1 : 1;
});