Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама
Новини 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.

25

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

Коментарі

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

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

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

Filament v5.10.0
Новини 12 жовтня 2026

Filament v5.10.0: сітка в rich editor, скелетони таблиць і десятки виправлень

Вийшов Filament v5.10.0: елементи керування сіткою в rich editor, допоміжний контент для віджетів-графіків, скелетони завантаження таблиць, submitOnCompletion() для OneTimeCodeInput та багато виправлень авторизації, доступності й стану.

18
Laravel AI SDK на практиці
Новини 09 жовтня 2026

Laravel AI SDK на практиці: агент, що стоїть за There There

Фрік Ван дер Гертен розповідає, як у Spatie побудували AI-шар хелпдеска There There на Laravel AI SDK: клас агента, інструменти з типізованою JSON-схемою та делегування роботи звичайним domain-діям.

16

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

ITExpert Нова
Вчора

Senior PHP Back-end Developer

Розробка нового функціоналу платформи з дотриманням найкращих практик якості коду, інтеграція із зовнішніми сервісами та проведення код-рев'ю. Ключові вимоги: досвід роботи з Symfony, NoSQL та мікросервісами. Формат роботи віддалений.

Middle PHP Developer

Розробка та підтримка додатків і мікросервісів на PHP, створення REST API, рефакторинг і оптимізація коду, інтеграція зі сторонніми сервісами. Основний стек: PHP від 3 років, Laravel або Symfony, MySQL/PostgreSQL, Redis, WebSocket. Потрібні знання SOLID, MVC та патернів проєктування, англійська на рівні читання технічної документації. Бажано досвід з мікросервісами, тестуванням (PHPUnit), чергами (RabbitMQ, Kafka) та React або Vue.

PHP, Symfony Developer (in-house)

Розробник на PHP 8.x і Symfony 7.3 займатиметься розвитком і підтримкою платформи: гостьове оформлення замовлення, Fuzzy Search, документообіг у кабінеті, захист форм від спаму, інтеграції з 1С, платіжною системою, Новою Поштою та Google Merchant Center. Також потрібне технічне SEO (hreflang, sitemap.xml, canonical) і налаштування staging-середовища. Вимоги: комерційний досвід PHP від 3 років, Doctrine ORM, MySQL, Twig, Docker, Git, великі каталоги та робота з API й ERP.

Пакети за темою

Kalimera

welldigit/kalimera

Interactive installer that scaffolds Laravel applications the Well Digit way.

v1.0.0 10