Где надо использовать выборку записей пачками (chunk) в Laravel?

Ответ

Метод chunk() в Laravel Eloquent — это инструмент для обработки больших наборов данных без перегрузки памяти. Я использую его в следующих сценариях:

  1. Обработка данных в Artisan-командах Например, для ежедневного пересчёта рейтинга пользователей.

    // Без chunk() это загрузит ВСЕХ пользователей в память
    User::where('active', true)->chunkById(1000, function ($users) {
        foreach ($users as $user) {
            $user->recalculateRating(); // Сложная логика
            $user->save();
        }
    });

    chunkById() предпочтительнее, так как устойчив к изменениям данных во время обработки.

  2. Генерация отчётов или экспорт данных При создании CSV-файла с десятками тысяч заказов.

    $stream = fopen('export.csv', 'w');
    Order::with('customer')->chunk(500, function ($orders) use ($stream) {
        foreach ($orders as $order) {
            fputcsv($stream, [$order->id, $order->customer->name]);
        }
    });
    fclose($stream);
  3. Пакетная отправка уведомлений или импорт данных из внешних API.

Важное замечание: chunk() не подходит для простых массовых обновлений (например, UPDATE users SET status = 'active' WHERE ...). Для этого гораздо эффективнее использовать DB::update() или Eloquent's update() метод, который выполнит один запрос к БД. chunk() нужен именно тогда, когда с каждой записью требуется совершить сложную логику на стороне PHP.

Ответ 18+ 🔞

А, ну вот, про chunk()! Это же, бля, классика, когда тебе нужно перелопатить овердохуища записей из базы, а память на сервере — хуй с горы, её на всех не хватит.

Представь себе, ты пишешь какую-нибудь админскую команду, которая должна пройтись по всем юзерам и что-то им посчитать. И если ты сделаешь просто User::all(), то, ёпта, твой скрипт накроется медным тазом, потому что он попытается засунуть в оперативку всех пользователей разом, а их там, допустим, миллион. Сервер просто ляжет и скажет «пиздец, я устал».

Вот тут-то и выручает chunk(). Он работает как умный грузчик: не тащит весь склад сразу, а берёт палетами. Скажем, по 1000 записей за раз.

User::where('active', true)->chunkById(1000, function ($users) {
    foreach ($users as $user) {
        $user->recalculateRating(); // Какая-то нетривиальная хуйня на PHP
        $user->save();
    }
});

Видишь? Он подгрузил первую тысячу активных юзеров, ты с каждым поотдельности наваял свою бизнес-логику (recalculateRating()), сохранил. Потом он подгрузил следующую тысячу, и так далее. Память не взрывается, все довольны. И обрати внимание — я использую chunkById(). Это, бля, важно! Обычный chunk() может нахуячить дублей или потерять записи, если данные во время работы меняются. А chunkById() идёт строго по первичному ключу, и с ним таких подлянок не случается.

Ещё отличный кейс — это когда тебе надо нагенерить какой-нибудь жирный CSV-отчёт или выгрузить данные в файл.

$stream = fopen('export.csv', 'w');
Order::with('customer')->chunk(500, function ($orders) use ($stream) {
    foreach ($orders as $order) {
        fputcsv($stream, [$order->id, $order->customer->name]);
    }
});
fclose($stream);

Опять же, не пытаемся впихнуть все заказы за год в массив, а спокойно, по 500 штук, пишем их в файловый поток. Элегантно и без геморроя.

Но вот тут, ёбана, важный момент, который многие проёбывают! Не надо быть распиздяем и использовать chunk() для простых массовых обновлений в БД. Смотри, если тебе надо всем юзерам проставить status = 'active', то делать вот так — это пиздопроебина чистой воды:

// НЕ ДЕЛАЙ ТАК, ТЫ ЖЕ НЕ ВРАГ СВОЕЙ БАЗЕ ДАННЫХ?
User::chunk(200, function ($users) {
    foreach ($users as $user) {
        $user->update(['status' => 'active']);
    }
});

Ты знаешь, что в этом случае произойдёт? Для каждой пачки в 200 юзеров ты сделаешь 200 отдельных UPDATE-запросов! Это же ёперный театр! Вместо одного красивого запроса UPDATE users SET status = 'active' WHERE ... ты устроишь своей базе настоящую истерику.

Для таких простых операций есть DB::update() или User::where(...)->update(...). chunk() — это твой тяжёлый инструмент на тот случай, когда с каждой записью нужно поотдельности поколдовать на стороне PHP. Вот тогда он — лучший друг. А так — включай голову, э бошка думай, прежде чем его применить.