ここ数年ですっかりPHPの代表的フレームワークの地位を日本でも確立しつつあるLaravel、実案件でも多くのサービスがLaravelで作られるようになりましたね。
今回は、そんなLaravelネタとして、タイトルの通りCollectionを使ってデータベースからデータをグループ化し、さらに特定のフィールドでソートする方法を紹介したいと思います。
環境
取り急ぎ、今回の環境は下記の通りとなります。
今回の環境
- Laravel 5.8.11
- MySQL 5.7.25
仕様と要点
まずは、今回の仕様として下記のようなテーブルがあると仮定します。なお、テーブル名は「foods」としておきます。
ID | category_name | name | price |
---|---|---|---|
1 | 果物 | りんご | 150 |
2 | 果物 | オレンジ | 100 |
3 | 果物 | いちご | 200 |
4 | 野菜 | キャベツ | 150 |
5 | 野菜 | トマト | 100 |
6 | 野菜 | レタス | 250 |
上の表のように「id」,「category_name」,「name」、「price」という4つのカラムから構成されているテーブルで、「果物」と「野菜」2つのカテゴリーに属する食べ物の名前とその金額が格納されているデータになります。
そして、このデータをカテゴリーグループに分けて、さらにグループ化された中のデータを金額の安い順番に並び替えて取得してみたいと思います。
Collectionを使って求めてみる
もちろん、Eloquentやクエリビルダーでも同様の結果を求めることは可能ですが少し複雑だったり、一旦取得したデータをforeach等を使って独自に並べ替える方法でも可能ですが、Collectionを使えばとてもシンプルかつ簡単に求めることができます。
Model
とりあえず、まずは今回はfoodsというテーブルからデータを取得するので、Foodモデルを下記のように作成しておきます。
Controller
モデルを作成したら実際データ取得のコードを書くControllerで読み込んでおきます。
use App\Food;
読み込んだら、実際にデータを取得してみます。
今回のようなデータ構成から、ソートされた状態でグループ化したい場合はいろいろと方法はあるのですが、とりあえず一番ベーシックな方法は下記のような感じかと思います。
$grouped = Food::all()->sortBy('price')->groupBy('category_name')->values()->all();
コードを見てもらうと分かると思いますが、「price」でソートして「category_name」でグループ化していることが分かると思います。Collectionを配列を操作するための機能なのでEloquentやクエリビルダーとは種類が異なりますが、Collectionだとここまで簡潔なコードで取得することが可能になります。
mapToGroupsを使ってキー名を指定
もちろん、上記の方法でも良いのですが、より分かりやすいかたちで結果を受け取りたい場合もあります。例えば結果のキーの名前を取得したい場合があると思います。そういった場合は「mapToGroups」を下記のように使うことで求めることができます。
$grouped = Food::all()->mapToGroups(function ($item, $key) {
return [$item->category_name => $item];
})->map(function ($value) {
return $value->sortBy('price')->values()->all();
});
この場合、mapToGroupsのコールバックで指定した「$item->category_name」がキー名になります。つまり、下記のようにカテゴリ名がキー名になるということですね。
Collection {#242 ▼
#items: array:2 [▼
"果物" => array:3 [▼
0 => Food {#259 ▶}
1 => Food {#258 ▶}
2 => Food {#260 ▶}
]
"野菜" => array:3 [▼
0 => Food {#262 ▶}
1 => Food {#261 ▶}
2 => Food {#263 ▶}
]
]
}
こうすることで見た目的にも分かりやすくなりますし、このデータを使って処理をする際にキー名をそのまま使えるという利点もあります。
takeを使って最安値を取得
さらに、上のコードにtakeを足して下記のようにすると、各グループの最安値を取得することも可能です。
$grouped = Food::all()->mapToGroups(function ($item, $key) {
return [$item->category_name => $item];
})->map(function ($value) {
return $value->sortBy('price')->take(1)->values()->all();
});
このようにsortByした後にtakeメソッドを使って「1」を指定してあげることで、最初のレコード、つまりこの場合最安値を取得するということになります。
ちなみに、sortByではなく「sortByDesc」を使うことで降順で取得することも可能です。この場合はpriceの高い順に並べ変えてくれます。
まとめ
ということで、今回はLaravelのCollectionを使ってグループ化したデータを、さらにソートする方法を紹介させていただきました。
配列処理は複雑になればなるほどコードも複雑化してしまいがちですが、Collectionを使うことでかなり簡略化できるので、覚えておいて損はないと思います。
てな感じで、今回はここまで!また!