- カテゴリ: collection
 - 掲載バージョン: Laravel 12・PHP 8.4
 - 名前空間 / FQCN: 
Illuminate\Support\Collection::groupBy - 関連: 
countBy/mapToGroups/partition/keyBy/sortBy - 変更履歴: なし
 
要点(TL;DR)
- 要素を同じキー(または計算結果)ごとにネストしたコレクションへまとめる
 - 使い方:
$users->groupBy('team_id') - 罠:
- 既定でキーは再採番(
$preserveKeys=false) - 文字列キーは dot記法(
user.team.id)で辿れ、見つからないとnullグループに入る - 多段グループは 
['year', 'status']の順序がそのままネスト順になる 
 - 既定でキーは再採番(
 
概要
groupBy は配列/オブジェクトの配列を、指定キー・クロージャ・複数条件で階層的に分類します。Eloquentの取得結果を「月×状態」「部署×職種」などでまとめ、後段で集計・整形に使うのが実務パターンです。クエリビルダの GROUP BY とは別物で、取得後のメモリ内処理です。
構文 / シグネチャ
public function groupBy(string|callable|array $groupBy, bool $preserveKeys = false): Illuminate\Support\Collection
- 引数(表)
 
| 引数 | 型 | 必須 | 既定値 | 説明 | 
|---|---|---|---|---|
| $groupBy | `string | callable | array` | ✓ | 
| $preserveKeys | bool | false | 各グループ内で元のキーを保持するか(false だと0始まりに再採番) | 
- 戻り値:
Collection(キー =>Collection)。配列指定時は多重のネストコレクション - 例外/副作用:
- コールバック内での例外はそのまま伝播
 - 文字列キーで未定義を辿ると 
nullキーにグループ化 LazyCollectionでも使用可だが全件を実体化するためメモリ使用量に注意
 
使用例
最小例
<?php
use Illuminate\Support\Collection;
$items = collect([
    ['type' => 'fruit', 'name' => 'apple'],
    ['type' => 'fruit', 'name' => 'banana'],
    ['type' => 'vegetable', 'name' => 'carrot'],
]);
$grouped = $items->groupBy('type'); // dot記法も可: 'user.team.id'
print_r($grouped->toArray());
// [
//   'fruit' => [
//     ['type' => 'fruit', 'name' => 'apple'],
//     ['type' => 'fruit', 'name' => 'banana'],
//   ],
//   'vegetable' => [
//     ['type' => 'vegetable', 'name' => 'carrot'],
//   ],
// ]
実務例:月×ステータスで売上集計
<?php
use Illuminate\Support\Collection;
use Carbon\Carbon;
use App\Models\Order;
// 例: 今月から過去6ヶ月の注文を取得
$orders = Order::query()
    ->where('created_at', '>=', now()->subMonths(6)->startOfMonth())
    ->get(['id', 'status', 'amount', 'created_at']);
$grouped = $orders->groupBy([
    fn ($o) => $o->created_at->format('Y-m'), // 月
    'status',                                 // ステータス
]);
// 各月×ステータスの件数と売上合計を算出
$summary = $grouped->map(fn ($byMonth) => $byMonth->map(function ($ordersByStatus) {
    return [
        'count' => $ordersByStatus->count(),
        'total_amount' => $ordersByStatus->sum('amount'),
    ];
}));
// 例: 2025-09 の paid 合計
$paidSep = data_get($summary, '2025-09.paid.total_amount', 0);
キー保持(preserveKeys)
$grouped = $items->groupBy('type', preserveKeys: true);
// 各グループ内で、元配列のキーを保持
よくある落とし穴・注意
- キー再採番:
$preserveKeys=falseでは各グループ内が 0,1,2… に再採番。元キーが必要ならtrue。 - 未定義経路の集約:
'user.team.id'が存在しない要素はnullグループに入る。??で補正するならコールバックを使う。 - 多段グループの順序:
['year','status']の順がそのまま ネスト順。アクセスは$groups->get($year)->get($status)。 - LazyCollectionの実体化:全件を保持するため大規模データではメモリ圧迫。必要ならDB側で 
GROUP BYし、結果を小さくしてからgroupByする。 - ソートは別:
groupByは並び替えない。並べたい場合はsortKeys()/sortBy()を合わせて使う。 
代替・関連APIとの比較
countBy:個数だけ欲しいならこちらが軽量(値→件数)。mapToGroups:コールバックでkey => valueのペア群を返し、1要素を複数のグループに振り分け可能。groupByは1層ごとに1キーへ分類。partition:真偽で 2分割するだけなら簡潔。keyBy:キーを付け替えるだけでグループ化はしない。
コレクション特性(カテゴリ追記)
- チェーン可:可
 - 破壊的/非破壊:非破壊(新しい 
Collectionを返す) - キー保持:オプション(
$preserveKeys) - LazyCollection:可(ただし全件実体化)
 - 計算量の目安:O(n)(nは要素数)
 
入出力対応(ミニサンプル)
| 入力 | 呼び出し | 出力(概念) | 
|---|---|---|
[['t'=>'A'],['t'=>'B'],['t'=>'A']] | ->groupBy('t') | ['A'=>[[…],[…]], 'B'=>[[…]]] | 
[['y'=>2025,'s'=>'paid'], …] | ->groupBy(['y','s']) | [2025=>['paid'=>[[…]], 'fail'=>[[…]]]] | 
テスト例(Pest)
<?php
use Illuminate\Support\Collection;
it('groups by key and closure', function () {
    $c = collect([
        ['team' => 'A', 'score' => 10],
        ['team' => 'B', 'score' => 20],
        ['team' => 'A', 'score' => 30],
    ]);
    $byTeam = $c->groupBy('team');
    expect($byTeam->keys())->toEqual(collect(['A','B']));
    expect($byTeam['A']->sum('score'))->toBe(40);
    $byHighLow = $c->groupBy(fn($x) => $x['score'] >= 20 ? 'high' : 'low');
    expect($byHighLow['high']->count())->toBe(2);
    expect($byHighLow['low']->count())->toBe(1);
});
トラブルシュート(エラー別)
| 症状/エラー | 原因 | 対処 | 
|---|---|---|
Call to a member function format() on null | created_at が null | optional($o->created_at)?->format('Y-m') か nullを別グループに分ける | 
| 期待したグループが空 | dot記法のパス違い / キー名誤り | data_get($item, 'path') で確認、またはクロージャで明示 | 
| 並びがバラバラ | groupBy はソートしない | ->sortKeys() や ->map(fn($g)=>$g->sortBy('…')) を併用 | 
| メモリ不足 | 大量データをすべて保持 | DBで GROUP BY → 少量結果に groupBy、もしくは countBy で代替 | 
参考リンク
- Laravel Docs — Collections: groupBy(公式): https://laravel.com/docs/12.x/collections#method-groupby
 - ソースコード(Illuminate\Support\Collection): https://github.com/laravel/framework/blob/12.x/src/Illuminate/Support/Collection.php
 - ヘルパ 
data_get(dot記法): https://laravel.com/docs/12.x/helpers#method-data-get - LazyCollection 概要: https://laravel.com/docs/12.x/collections#lazy-collections
 

  
  
  
  