【Laravel】Collectionを使ってデータをグループ化し、さらに特定のフィールドで並び替える方法

 ここ数年ですっかり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を使うことでかなり簡略化できるので、覚えておいて損はないと思います。

 てな感じで、今回はここまで!また!

お仕事のご相談・ご依頼
お気軽にお問い合わせください!

お仕事の依頼はこちら

シェアありがとうございます!

タグ
laravel PHP

著者プロフィール

Taka

東京、奄美大島を拠点にサーフィンとスノーボードが好きなフリーランスのWebクリエイターです。普段はプログラム書いたりデザインしたり映像作ったりしています。いろいろな人の話しを聞くのが好きなので、このブログを通して多くの人と繋がりが出来たら嬉しいです。noteとInstagramもやっているのでフォローしてくれたらありがたいです!

人気記事

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です