laravelのCollectionで複数カラムでのソート


通常のorderBy

ID降順でソート

$items = [
    [
        'id' => 1,
        'datetime' => '2021-06-01',
    ],
    [
        'id' => 2,
        'datetime' => '2021-06-02',
    ],
    [
        'id' => 3,
        'datetime' => '2021-06-01',
    ],
    [
        'id' => 4,
        'datetime' => '2021-06-02',
    ],
];

$items = collect($items)->sortByDesc('id')->toArray();
dd($items);

結果

array:4 [
  3 => array:2 [
    "id" => 4
    "datetime" => "2021-06-02"
  ]
  2 => array:2 [
    "id" => 3
    "datetime" => "2021-06-01"
  ]
  1 => array:2 [
    "id" => 2
    "datetime" => "2021-06-02"
  ]
  0 => array:2 [
    "id" => 1
    "datetime" => "2021-06-01"
  ]
]

複数カラムでソート

datetime降順、ID昇順でソート


$items = [
    [
        'id' => 1,
        'datetime' => '2021-06-01',
    ],
    [
        'id' => 2,
        'datetime' => '2021-06-02',
    ],
    [
        'id' => 3,
        'datetime' => '2021-06-01',
    ],
    [
        'id' => 4,
        'datetime' => '2021-06-02',
    ],
];

$res = collect($items)->sortBy([
    ['datetime', false], // [カラム名, 昇順でソートするか]
    ['id', true]
])->toArray();
dd($res);

結果

array:4 [
  0 => array:2 [
    "id" => 2
    "datetime" => "2021-06-02"
  ]
  1 => array:2 [
    "id" => 4
    "datetime" => "2021-06-02"
  ]
  2 => array:2 [
    "id" => 1
    "datetime" => "2021-06-01"
  ]
  3 => array:2 [
    "id" => 3
    "datetime" => "2021-06-01"
  ]
]

おまけ

sortBy()の内部ではsortByMany()という関数が呼ばれています。このメソッドの中のis_callable($prop)というところでカラム名がcallableかどうかを確かめています。つまり、dateなどの組み込み関数と同じ名前がソートするカラム名に含まれているとエラーが発生します。気をつけましょう。

    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 = [data_get($a, $prop), data_get($b, $prop)];

                    if (! $ascending) {
                        $values = array_reverse($values);
                    }

                    $result = $values[0] <=> $values[1];
                }

                if ($result === 0) {
                    continue;
                }

                return $result;
            }
        });

        return new static($items);
    }
``