【Laravel】【collection】コレクションでSQLの集計クエリがしたい→ちょっとの工夫で簡単でした

2021年9月29日

こんにちは。最近LaravelのコレクションでSQLの集計をする要件がありました。その時にコレという記事が無かったのでまとめておこうと思います。

やりたいこと

group byで指定した集計単位でcount, average, sumのような集計関数を使って集計します。SQLでは頻出のパターンですね。

用意するデータ

今回はtinkerを使って検証します。まずはコレクションを用意します。部署名(busho)、名前(name), 年齢(age)の3項目を持つデータを4件登録しておきます。

=> Illuminate\Support\Collection {#4298
     all: [
       [
         "busho" => "system1",
         "name" => "sato",
         "age" => 29,
       ],
       [
         "busho" => "system1",
         "name" => "tanaka",
         "age" => 23,
       ],
       [
         "busho" => "system2",
         "name" => "ishida",
         "age" => 45,
       ],
       [
         "busho" => "system2",
         "name" => "tanahashi",
         "age" => 25,
       ],
     ],
   }

集計する

部署別の平均年齢を算出したいと思います。前述のコレクションを集計していきます。以下の2段階で説明します。

  • groupByメソッドでコレクションを部署単位でまとめる
  • eachで部署単位に集計関数を実行

groupByで部署単位にまとめる

まずはgroupByを実行します。groupByは部署単位に新たにコレクションを作り、それらをまとめて1つのコレクションにします。元々のデータではbushoにsystem1とsystem2の2種が登録されていました。groupByするとsystem1とsystem2のコレクションを作ります。そしてその2つのコレクションを一つのコレクションとしてまとめています。

>>> $groupByBusho = $collection->groupBy('busho');
=> Illuminate\Support\Collection {#4350
     all: [
       "system1" => Illuminate\Support\Collection {#4364
         all: [
           [
             "busho" => "system1",
             "name" => "sato",
             "age" => 29,
           ],
           [
             "busho" => "system1",
             "name" => "tanaka",
             "age" => 23,
           ],
         ],
       },
       "system2" => Illuminate\Support\Collection {#4363
         all: [
           [
             "busho" => "system2",
             "name" => "ishida",
             "age" => 45,
           ],
           [
             "busho" => "system2",
             "name" => "tanahashi",
             "age" => 25,
           ],
         ],
       },
     ],
   }

集計関数を部署毎に実行する

部署ごとにまとめたコレクションに対してeachメソッドを実行します。eachメソッドはforeachのようにコレクションの全件をループ処理してくれます。この場合はsystem1とsystem2の2件をループします。このループの中でsystem1と、system2の年齢に対してavgメソッド実行し平均を算出します。予め作成しておいたbushoAgeAverageというコレクションにpushしています。

>>> $bushoAgeAverage = collect();
>>> $groupByBusho->each(function($item,$key) use($bushoAgeAverage){
            $bushoAgeAverage->push([
                'busho' => $key,
                'ageAvg' => $item->avg('age')
            ]);
         });

こうして部署別の年齢平均のコレクションを作ることができました。

>>> $bushoAgeAverage;
=> Illuminate\Support\Collection {#4208
     all: [
       [
         "busho" => "system1",
         "ageAvg" => 26,
       ],
       [
         "busho" => "system2",
         "ageAvg" => 35,
       ],
     ],
   }

Laravel

Posted by kobainmac