chunk — コレクションを指定サイズで分割

collection
  • カテゴリ: collection
  • 対応バージョン: Laravel 11・12・PHP 8.2
  • 名前空間 / FQCN / コマンド: Illuminate\Support\Collection::chunk
  • 関連: chunkWhile / split / splitIn / forPage / Eloquent\Builder::chunk
  • 変更履歴: ―

要点(TL;DR)

  • コレクションをサイズごとに小分け(チャンク)する
  • collect([1,2,3,4,5])->chunk(2)
  • 罠: 巨大データを丸ごと読み込み→メモリ逼迫/DBの chunk と混同キーは保持(必要なら values() で詰め直す)

概要

chunk($size) は、元の Collection を指定件数ごとに区切り、「コレクションのコレクション」を返します。グリッド描画やバッチ処理の分割などで便利です。大量データは Eloquent\Builder::chunk()LazyCollection と組み合わせてストリーミング処理に切り替えましょう。

構文 / シグネチャ

use Illuminate\Support\Collection;

public function Collection::chunk(int $size): Collection
  • 引数
引数必須既定値説明
$sizeintなし1 以上の分割サイズ。最後のチャンクは不足分を含む
  • 戻り値Collection<Collection<mixed>>(各要素は小分け後のコレクション)
  • 例外/副作用$size < 1ValueError(内部で array_chunk 相当を使用)。副作用なし(非破壊)。

使用例

最小例

<?php

use Illuminate\Support\Facades\Route;

Route::get('/demo/chunk', function () {
    $chunks = collect(['a' => 10, 'b' => 20, 'c' => 30, 'd' => 40, 'e' => 50])->chunk(2);

    // キーは各チャンク内で保持される
    return $chunks->map(fn ($c) => $c->all())->all();
});
// 出力例: [
//   ['a' => 10, 'b' => 20],
//   ['c' => 30, 'd' => 40],
//   ['e' => 50],
// ]

実務例(通知を 100 件ごとにバッチ化)

use App\Models\User;
use Illuminate\Support\Facades\Bus;

$jobs = collect(User::query()->whereNotNull('email')->pluck('id'))
    ->chunk(100) // 100件ずつ分割(※全件pluckはメモリに乗る点に注意)
    ->map(fn ($ids) => new \App\Jobs\SendNotificationBatch($ids->all()));

Bus::batch($jobs)->dispatch();

大規模データは LazyCollection でストリーミング

use App\Models\User;

// メモリ節約: DB から逐次カーソル取得 → 100件ずつチャンク
User::cursor()
    ->chunk(100) // LazyCollection の chunk
    ->each(function ($chunk) {
        // $chunk は通常の Collection。ここで処理やジョブ投入
        dispatch(new \App\Jobs\SendNotificationBatch($chunk->pluck('id')->all()));
    });

コレクション特性(collection ルール)

  • チェーン可否: 可(chunk()->map()->each() 等)
  • 破壊的/非破壊: 非破壊(元コレクションは変更しない)
  • キー保持: 保持する(各チャンク内で元キーを維持)
  • LazyCollection 対応: cursor()->chunk() で低メモリ処理)
  • 計算量の目安: O(n)(n は要素数)

入出力対応(小さなサンプル)

入力chunk(3) の出力(概念)
['a'=>1,'b'=>2,'c'=>3,'d'=>4][[a=>1,b=>2,c=>3],[d=>4]]
[0=>1, 2=>2, 5=>3][[0=>1,2=>2,5=>3]](キーは保持)

よくある落とし穴・注意

  • DB の chunk() と混同Eloquent/Query\Builder::chunk($size, $callback)逐次的に DB から取得Collection::chunk()すでにメモリ上のデータを分割。
  • メモリ使用量:巨大配列をいきなり collect()->chunk() すると負荷増。cursor()LazyCollection::chunk() へ。
  • キー詰め直し:UI の連番インデックスが必要なら、$chunk->values() を併用。
  • ネスト形状:戻りは「コレクションの配列」ではなく**「コレクションのコレクション」**。配列が欲しければ ->map->all()->all()

代替・関連APIとの比較

  • chunkWhile(callable):条件が変わるまで同じチャンクに連結。内容ベースで区切りたいとき。
  • split(int $number) / splitIn(int $number)分割数を先に決めて均等割り(端数調整あり)。
  • forPage(int $page, int $perPage):特定ページだけ切り出し(単一ページ抽出)。
  • Eloquent\Builder::chunk(int $size, callable $cb):DB から逐次取得してコールバック処理。超大規模データはこれ。

選定基準

  • 固定サイズで区切って全部回す」→ chunk($size)
  • 条件の変化点で区切る」→ chunkWhile()
  • N 分割にしたい」→ split()/splitIn()
  • ページングの一部だけ」→ forPage()
  • DB 超大量データ」→ Eloquent::chunk() or cursor()+LazyCollection::chunk()

テスト例(Pest)

<?php

use Illuminate\Support\Collection;

it('splits collection with preserved keys', function () {
    $chunks = collect(['a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5])->chunk(2);

    expect($chunks)->toHaveCount(3);
    expect($chunks->first()->keys()->all())->toBe(['a','b']);
    expect($chunks->last()->all())->toBe(['e' => 5]);
});

トラブルシュート(エラー別)

症状/エラー原因対処
ValueError: ... length must be greater than 0$size が 0 以下chunk(1以上) を指定
メモリ不足 / 非常に遅い全件を collect() してから chunk()cursor()LazyCollection::chunk() または Eloquent::chunk() を使用
想定と違うインデックスキー保持仕様を失念各チャンクで values() を呼び連番化
期待した配列形でないネストが Collection<Collection>->map->all()->all() で配列へ変換

参考リンク

レン (Wren)

こんにちは。レンです。

Laravelのコードの森に住んでいる、小さな案内役です。
ルーティングの枝やクラスの影を歩きながら、コードの流れや仕組みを眺めています。

このサイトでは、Laravelの基本から実装のコツまで、開発で役立つポイントを静かに整理しています。
難しいことを増やすのではなく、コードの見通しが少し良くなるヒントを届けるのが役目です。

「この処理はどこに書くのがいいのか」
「Laravelではどう考えると整理できるのか」

そんな疑問に、小さなメモを残すような気持ちで記事を書いています。

コードを書いている途中で迷ったとき、
このサイトが少し立ち止まって整理できる場所になればうれしいです。

レン (Wren)をフォローする