Laravel Collectionで任意の配列の順にソートするメソッドを作成する
calendar_today
2023-02-13
insights
views: 1503
thumbnail images

これなに

Laravel Collectionで使える、任意の配列に並び替えることができるマクロを作成した。これでデータベースに並び順位列を作成したりしなくてよくなる。また、json配列と併用し、並び順テーブルを用意して複数の配列やコレクションに並び順を当てることができる。
Collectionには、Laravel側で予め用意されているもの以外に、自身でメソッドを作成できる機能が備わっている。コレクションの拡張 - Laravel 9.x コレクション これを使用して、今回はsortByArrayというメソッドを作成してみようと思う。

環境

  • Laravel 9.x

=> php7.4未満は、ラムダ式部分を通常の無名関数に書き換えることで対応可能

実装方法

1. プロバイダーを作成する

自作のメソッドを定義しておくためのproviderを用意する。

php artisan make:provider MacroServiceProvider

2. メソッドを定義する

ターゲットプロパティと配列を受け取り、配列順に並び替えるプロパティを作成する
boot()に記載する

public function boot()
{
    Collection::macro('sortByArray', function (string $target, array $orders) {
        $sorted = collect([]);
        collect($orders)->each(function ($order) use (&$sorted, $target) {
            $sorted = $sorted->merge($this->filter(fn ($item) => $item->{$target} === $order));
        });
        $sorted = $sorted->merge($this->filter(fn ($item) => !in_array($item->{$target}, $orders, true)));
        return $sorted;
    });
}

これだけ。
※配列形式のキーを指定する可能性がある場合は、$item->{$target}$item[$target]としてください。

使用方法

例えば、ユーザの名字を任意の配列順で並び替えたいとき。

$order = ['田中', '佐藤', '鈴木'];
$users = collect([
    ['last_name' => '佐藤', 'first_name' => '一郎'],
    ['last_name' => '鈴木', 'first_name' => '二郎'],
    ['last_name' => '田中', 'first_name' => '三郎'],
]);

$users->sortByArray('last_name', $order);

// [
//     0 => ['last_name' => '田中', 'first_name' => '三郎'],
//     1 => ['last_name' => '佐藤', 'first_name' => '一郎'],
//     2 => ['last_name' => '鈴木', 'first_name' => '二郎'],
// ]

指定した並び順にない文字列が来る場合、ソートされたアイテムの後ろに並び順を保持したまま結合される。

$order = ['田中', '鈴木'];
$users = collect([
    ['last_name' => '佐藤', 'first_name' => '一郎'],
    ['last_name' => '鈴木', 'first_name' => '二郎'],
    ['last_name' => '田中', 'first_name' => '三郎'],
    ['last_name' => '加藤', 'first_name' => '四郎'],
]);

$users->sortByArray('last_name', $order);

// [
//     0 => ['last_name' => '田中', 'first_name' => '三郎'],
//     1 => ['last_name' => '鈴木', 'first_name' => '二郎'],
//     2 => ['last_name' => '佐藤', 'first_name' => '一郎'],
//     3 => ['last_name' => '加藤', 'first_name' => '四郎'],
// ]

さらに

また、以下のようなjson型のカラムを1つ、識別するnameカラムを1つ追加したordersテーブルを作成して

id name varchar(255) order json
1 last_name ["田中", "佐藤", "鈴木"]

以下のように取得することで、管理画面等で並び順を編集することもできるかと思う。

$order = Order::where('name', 'last_name')->first()->order;
$users->sortByArray('last_name', $order);

上記の利点は、通常だとテーブルにorderカラムを用意したり、テーブルに対応したuser_ordersテーブルのようなものを用意する必要があると思うが、これだとordersテーブル1つで様々なテーブルや配列の並び順を、直感的に保存することができる点。
その際は、nameカラムでテーブル名とカラム名をドット記法で記述するのも手だと思う。

id name varchar(255) order json
1 users.last_name ["田中", "佐藤", "鈴木"]
2 tags.name ["TailwindCSS", "Alpine.js", "Laravel", "Livewire"]

これで「部署をこういう順で並び替えて!」とか「タグを知名度順で並べて!」に簡単に対応できかなり重宝すると思うので、ぜひとも使用してほしい。

楽しいCollectionライフを!(?)

calendar_today
2023-02-13
insights
views: 1503