はじめに

Laravel で chunk もしくは chunkById を使いながらトータル件数を計算する方法。

TL;DR

この記事が参考になった方
ここここからチャージや購入してくれると嬉しいです(ブログ主へのプレゼントではなく、ご自身へのチャージ)
欲しいもの / Wish list

目次

  1. はじめに
  2. TL;DR
  3. 環境・条件
  4. 詳細
    1. 前置き: どのような状況を想定しているか
      1. ダメな例1
      2. ダメな例2
    2. 対応方法
  5. まとめ
  6. その他・メモ
  7. 参考文献

環境・条件

1
2
3
4
5
6
7
8
9
10
11
12
$ cat /etc/os-release
PRETTY_NAME="Ubuntu 16.04.6 LTS"

$ php -v
PHP 7.3.16-1+ubuntu16.04.1+deb.sury.org+1 (cli) (built: Mar 20 2020 13:51:21) ( NTS )

$ composer -V
Composer version 1.9.1 2019-11-01 17:20:17

$ composer info laravel/framework
name : laravel/framework
versions : * v5.7.28

詳細

前置き: どのような状況を想定しているか

Laravel で大量のデータを処理する際、get()all() を使うと大量のメモリが必要となるため、一定件数ずつ処理する際には chunkchunkById を使うことになる。

例えば CSV へのデータ書き出し時に、データ件数(1〜n)も合わせて出力したいようなケースを想定。

以下はダメな例なので読み飛ばしても OK

ダメな例1

これだと foreach ごとに $i が初期化されるので 1, 2, … 100 の後にまた 1, 2, … と繰り返してしまう。

1
2
3
4
5
User::chunk(100, function ($users) use ($file) {
foreach ($user as $i => $user) {
Csv::write($file, [$i + 1, $user->name, ...];
}
});

ダメな例2

例1の反省を活かして chunk の外側にカウンタを定義。

一見問題なさそうにも見えるが、これも例1と同じ結果になってしまう。
※PHP に詳しくないので間違っているかもしれないが、おそらくチャンク(100件)ごとにコールバック(function ...)の部分が実行される(毎回 use ($i) で値渡しとなり 1 が入る)のだと思う。

1
2
3
4
5
6
7
$i = 1;
User::chunk(100, function ($users) use ($file, $i) {
foreach ($user as $user) {
Csv::write($file, [$i, $user->name, ...];
$i ++;
}
});

対応方法

参考: php - How can I get the return value of a Laravel chunk? - Stack Overflow

値渡しではなくリファレンス渡し(参照渡し)すれば OK

1
2
3
4
5
6
7
$i = 1;
User::chunk(100, function ($users) use ($file, &$i) {
foreach ($user as $user) {
Csv::write($file, [$i, $user->name, ...];
$i ++;
}
});

参照するだけなら chunk で良いが、更新するなら chunkById を使った方が良いので注意。

1
2
3
4
5
User::chunkById(100, function ($users)  {
foreach ($user as $user) {
// ...
}
});

まとめ

その他・メモ

chunk 以外にも cursor ってメソッドもあるので、覚えておくと良い。以下の記事が参考になる。

参考文献

関連記事

この記事が参考になった方
ここここからチャージや購入してくれると嬉しいです(ブログ主へのプレゼントではなく、ご自身へのチャージ)
欲しいもの / Wish list