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

Laravel: колекції й хелпери

20 питань · ~20 хв · Версія v3.0

Увійдіть, щоб продовжити

Методи колекцій і повідомлення вищого порядку, ліниві колекції, макроси, хелпери масивів, рядків і чисел, retry і rescue, Pipeline - питання всіх рівнів, від junior до lead.

За спробу
20
У пулі
44
Проходжень
0
Середній бал
-
Пройшли на 70%+
-

Питання для підготовки

16 питань

Collection - обгортка над масивом із плавним, ланцюжковим API. Eloquent-запити повертають саме колекції.

$names = collect($users)
    ->filter(fn ($u) => $u->active)
    ->sortBy('name')
    ->map(fn ($u) => $u->name)
    ->values();

Десятки методів: map, filter, reduce, groupBy, pluck, each, sum. Код читається зрозуміліше за вкладені цикли й array_*-функції. Для дуже великих наборів є LazyCollection (на генераторах).

Докладніше в документації: Колекції

LazyCollection використовує PHP-генератори, щоб тримати в пам'яті лише один елемент за раз - критично для величезних наборів.

LazyCollection::make(function () {
    $handle = fopen('huge.csv', 'r');
    while (($line = fgets($handle)) !== false) {
        yield $line;
    }
})->filter(...)->take(100)->each(...);

З Eloquent:

User::cursor()->each(function ($user) { /* по одному рядку */ });

На відміну від звичайної Collection, методи не матеріалізують увесь набір - обчислення «ліниві» й виконуються лише при ітерації.

Докладніше в документації: Lazy Collections

Колекції зручні настільки, що ними легко почати робити роботу бази - і заплатити памʼяттю та часом.

Типова помилка:

$active = Post::all()->where('is_published', true);        // вся таблиця в памʼять
$count  = Post::all()->count();                            // теж уся таблиця
$latest = Post::all()->sortByDesc('created_at')->take(5);  // і сортування в PHP

Кожен рядок витягує всі записи, щоб потім відкинути більшість.

Те саме в базі:

$active = Post::where('is_published', true)->get();
$count  = Post::count();
$latest = Post::latest()->limit(5)->get();

Різниця не косметична: база фільтрує за індексом і повертає стільки, скільки потрібно.

Як розрізняти. Методи Builder виконуються в SQL, методи Collection - у PHP. Межа проходить там, де стоїть get(), all() чи first(): усе після них - уже PHP.

Плутанину створює однакова назва: where() є і в білдера, і в колекції. Post::where(...) фільтрує в базі, Post::all()->where(...) - у памʼяті.

Коли колекція доречна:

  • Дані вже отримані, і потрібне ще одне групування чи перетворення - другий запит дорожчий.
  • Логіка не виражається в SQL: складна умова на PHP, робота з обʼєктами-значеннями.
  • Набір свідомо малий - десятки рядків, де різниці немає.

Коли ні: фільтрація, підрахунок, сортування й обмеження на таблиці, розмір якої росте. Це робота бази.

Проміжний варіант - lazy(): обхід пачками з синтаксисом колекції, коли обробити треба всі рядки, але тримати їх у памʼяті не можна.

Докладніше в документації: Колекції

Вкладені масиви (конфігурація, JSON зі стороннього API, payload вебхука) зручно читати крапковою нотацією замість ланцюжка isset:

use Illuminate\Support\Arr;

$data = [
    'user' => ['profile' => ['name' => 'Olena']],
    'orders' => [['total' => 10], ['total' => 20]],
];

Arr::get($data, 'user.profile.name');          // 'Olena'
Arr::get($data, 'user.email', 'n/a');          // 'n/a' - значення за замовчуванням
Arr::has($data, 'user.profile.name');          // true

data_get - те саме, але для масивів і об'єктів разом і з підтримкою *:

data_get($data, 'orders.*.total');             // [10, 20]
data_get($request->json(), 'payment.status');
data_get($model, 'author.profile.city');       // працює й з Eloquent-моделями

Запис:

Arr::set($config, 'mail.from.address', 'hello@example.com');   // за посиланням
data_set($data, 'orders.*.paid', false);       // усім елементам
data_fill($data, 'meta.source', 'api');        // лише якщо ключа ще немає
Arr::forget($data, 'user.password');

Інші корисні методи:

Метод Що робить
Arr::only($a, ['name', 'email']) лише вказані ключі
Arr::except($a, ['password']) усі, крім вказаних
Arr::wrap($value) гарантує масив: null → [], 'a' → ['a']
Arr::dot($a) / Arr::undot($a) вкладений масив ↔ плаский з ключами a.b.c
Arr::pluck($a, 'user.name') значення поля з кожного елемента
Arr::first($a, fn ($v) => ...) перший елемент за умовою
Arr::string($a, 'key'), Arr::integer(...) значення з перевіркою типу, інакше виняток

Пастка крапкової нотації: якщо в ключі масиву справді є крапка ('version.major' => 1), Arr::get($a, 'version.major') спершу перевіряє точний ключ і знайде його, але data_get і запис через Arr::set трактуватимуть крапку як вкладеність. Для таких даних краще звичайний доступ $a['version.major'].

Arr::wrap особливо корисний для параметрів, що приймають «одне значення або масив»: foreach (Arr::wrap($recipients) as $email) - без перевірок типу.

Для колекцій є ті самі можливості: collect($data)->get(...), ->pluck('user.name'), ->dot(). Якщо дані обробляються ланцюжком перетворень - колекція зручніша, для одного звернення - Arr чи data_get.

Докладніше в документації: Хелпери: data_get

Усі три повертають нову колекцію (чи значення) і не змінюють вихідну.

$prices = collect([100, 250, 40]);

$withVat = $prices->map(fn (int $p) => $p * 1.2);          // [120, 300, 48]
$expensive = $prices->filter(fn (int $p) => $p > 50);      // [0 => 100, 1 => 250]
$total = $prices->reduce(fn (int $sum, int $p) => $sum + $p, 0);   // 390

$prices->all();   // [100, 250, 40] - без змін
  • map() - перетворює кожен елемент, кількість та сама;
  • filter() - залишає елементи, що пройшли умову (без замикання - прибирає «порожні»);
  • reduce() - зводить колекцію до одного значення будь-якого типу.

Пастка filter(): ключі зберігаються - вийде [0 => 100, 1 => 250], а після видалення середнього елемента було б [0 => ..., 2 => ...]. У JSON такий масив стане об'єктом. Перед відповіддю викликають values().

Мутуючі методи теж є, і їх варто знати, щоб не здивуватися: transform(), push(), put(), pop(), shift(), forget() змінюють саму колекцію.

Для простих сум є готові sum(), avg(), max() - reduce() потрібен для складнішого накопичення.

Докладніше в документації: Метод map

Прочитати - ще не значить знати

20 питань, по одному на екран, ~20 хв. Після завершення - розбір кожної помилки з посиланням на питання.