Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама
Новини 04 вересня 2026

Групування сусідніх елементів колекції в Laravel за допомогою chunkBy()

Laravel 13.30 представляє метод chunkBy(), який є зручним скороченням для найпоширенішого варіанту використання chunkWhile():

$products->chunkWhile(fn ($value, $key, $chunk) => $value->parent == $chunk->last()->parent);

Тепер це можна написати простіше:

$products->chunkBy('parent');

До цього: chunkWhile() та порівняння

Як показано вище, до Laravel 13.30 можна було використовувати chunkWhile() для цього завдання. Цей метод приймає callback, який отримує поточне значення, його ключ та chunk, що формується. Він починає новий chunk щоразу, коли callback повертає false:

$lineItems->chunkWhile(
    fn ($value, $key, $chunk) => $value->order_id == $chunk->last()->order_id
);

Цікава частина цього рядка - одне слово (order_id), приховане в порівнянні між значенням та $chunk->last(). Метод chunkBy() приймає ключ або callback і будує порівняння за вас:

$lineItems->chunkBy('order_id');
$lineItems->chunkBy(fn ($item) => $item->order_id);

Ключ обробляється через data_get(), тому точкова нотація дозволяє доступ до вкладених масивів та об'єктів:

$users->chunkBy('address.city');

Сусідні, а не згруповані

Відмінність від groupBy() - це ключовий момент для розуміння, оскільки методи повертають однакову структуру і відрізняються лише порядком ваших даних:

collect([1, 1, 2, 2, 1, 1])->chunkBy(fn ($v) => $v);
// [[1, 1], [2, 2], [1, 1]]

collect([1, 1, 2, 2, 1, 1])->groupBy(fn ($v) => $v);
// [1 => [1, 1, 1, 1], 2 => [2, 2]]

Метод chunkBy() створює три chunks, оскільки дві послідовності 1 не знаходяться поруч. Це не баг, а властивість, яка робить метод ефективним. Якщо несусідні елементи з однаковим значенням мають опинитися разом, дані не відсортовані так, як потрібно chunkBy() - спочатку відсортуйте їх або використовуйте groupBy().

Ключі зберігаються всередині кожного chunk:

collect(['a' => 1, 'b' => 1, 'c' => 2])->chunkBy(fn ($v) => $v);
// [['a' => 1, 'b' => 1], ['c' => 2]]

Викличте values() на chunk, якщо потрібен список.

Потокова обробка відсортованого запиту

Метод chunkBy() додано як до звичайних колекцій, так і до LazyCollection. Оскільки chunkBy() успадковує ліниве виконання chunkWhile(), на ледачій колекції він повертає кожен chunk одразу після зміни значення і ніколи не тримає в пам'яті більше одного поточного chunk.

Розгляньмо експорт CSV для кожного замовлення з таблиці з кількома мільйонами позицій. З groupBy() усі рядки зберігаються в пам'яті до запису першого файлу. З cursor та chunkBy() найвища точка використання пам'яті припадатиме на найбільше окреме замовлення:

use App\Models\LineItem;
use Illuminate\Support\Facades\Storage;

LineItem::query()
    ->orderBy('order_id')
    ->orderBy('id')
    ->cursor()
    ->chunkBy('order_id')
    ->each(function ($items) {
        $orderId = $items->first()->order_id;
        Storage::disk('exports')->put(
            "orders/{$orderId}.csv",
            $items->map(fn ($item) => implode(',', [
                $item->sku,
                $item->quantity,
                $item->unit_price,
            ]))->implode(PHP_EOL)
        );
    });

Важливо: orderBy('order_id') - це не прикраса, а контракт, на якому працює chunkBy(). База даних виконує сортування за індексом, а PHP - розділення, по одному рядку за раз.

Та сама структура працює з лог-файлом:

use Illuminate\Support\LazyCollection;

LazyCollection::make(function () {
    $handle = fopen(storage_path('logs/laravel.log'), 'r');
    while (($line = fgets($handle)) !== false) {
        yield $line;
    }
})
->chunkBy(fn ($line) => str_contains($line, 'ERROR') ? 'error' : 'other')
->each(function ($block) {
    // Кожен блок — це послідовність рядків з помилками або без них
});

Або з API з пагінацією, або з генератора, що читає CSV. Скрізь, де джерело відсортоване і більше за обсяг пам'яті, chunkBy() перетворює групування на потокову операцію.

Дві речі, які варто знати

Порівняння нестроге. Реалізація порівнює отримані значення через ==, а не ===:

collect(['1', 1, 1.0])->chunkBy(fn ($v) => $v);
// один chunk

Для стовпця бази даних, що повертає послідовний тип, це не проблема. Для змішаного вводу це може об'єднати chunks, які ви очікували окремими. За потреби поверніть нормалізоване значення з callback:

$rows->chunkBy(fn ($row) => (string) $row['code']);

Два об'єкти порівнюються як рівні, коли вони одного класу з рівними властивостями, що зазвичай є бажаною поведінкою при розбитті за об'єктом-значенням.

Резолвер виконується двічі на елемент. Кожна перевірка границі обробляє поточний елемент і повторно обробляє останній елемент chunk. Якщо callback дорогий, наприклад парсинг дати чи хеш, спочатку обчисліть значення:

$entries
    ->map(fn ($entry) => [$entry, Carbon::parse($entry->logged_at)->toDateString()])
    ->chunkBy(fn ($pair) => $pair[1]);

Для простого пошуку ключа або властивості це не має значення.

Крайні випадки

Порожня колекція повертає порожню колекцію. Один елемент повертає один chunk, що містить його. Як eager, так і lazy колекції повертають той самий клас, на якому їх викликано, тому chunkBy() на LazyCollection дає LazyCollection з екземплярів LazyCollection.

Метод додано завдяки @JosephSilber у #61357.

7

Читати в документації

Коментарі

Увійдіть, щоб залишити коментар

Будьте першим, хто залишить коментар!

Читайте також

PayZephyr
Новини 12 вересня 2026

PayZephyr: Єдиний API для роботи з Stripe, Paystack та PayPal

PayZephyr - Laravel-пакет від Nwaneri Chukwunyere Kenneth, який об'єднує вісім платіжних провайдерів під одним зручним API. Підтримує автоматичне перемикання, захист від подвійної оплати, підписки та повернення коштів.

2
Laravel Telescope
Новини 11 вересня 2026

Artisan-команди для дебагу в Laravel Telescope 5.24.0

Laravel Telescope 5.24.0 додає дві нові Artisan-команди для роботи із записами із терміналу. Тепер можна переглядати запити, винятки, джоби та запити до бази даних без відкриття веб-інтерфейсу, а також отримувати дані у форматі JSON для скриптів та AI-агентів.

3

Вакансії за темою

Full Stack Developer (PHP, React, Middle, Middle+)

Full Stack розробник для підтримки та розвитку аналітичного продукту перевірки контрагентів. Робота зі складною бізнес-логікою, базами даних та інтеграцією AI-рішень. Стек: PHP 8.x (Laravel/Symfony), React, MySQL, REST API. Вимоги: 3+ років комерційного досвіду, глибоке розуміння SQL, Git, Docker, CI/CD, Linux, OWASP.

Digis Нова
Вчора

Senior Full-stack (PHP + React) Developer | Warsaw hybrid

Senior Full-stack розробник для великої SaaS-платформи в e-commerce. Розробка landing pages на React, інтеграція платіжних систем (Stripe), робота з Shopify API, підтримка PHP/WordPress, поступова міграція на сучасну архітектуру. Вимоги: 5+ років досвіду, 3+ років PHP та ReactJS, знання WordPress, англійська Upper-Intermediate+.

Програміст PHP (інтерн)

Вакансія на посаду інтерна PHP розробника для початківців з теоретичною базою ООП та базовими знаннями PHP. Потрібні навички Git/GitHub, власні проєкти. Стажування в офісі під керівництвом менторів з перспективою переходу на посаду Junior разробника.