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

Middle: питання на співбесіді з теми «Колекції»

Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.

3 питання

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

Перетворити один зі списків на «словник» за ключем - тоді пошук стає миттєвим.

// O(n × m): для кожного замовлення шукаємо клієнта перебором
foreach ($orders as $order) {
    $customer = $customers->firstWhere('id', $order->customer_id);
}

// O(n + m): один раз будуємо індекс
$customersById = $customers->keyBy('id');

foreach ($orders as $order) {
    $customer = $customersById[$order->customer_id] ?? null;
}

На 5000 замовлень і 5000 клієнтів перший варіант - до 25 мільйонів порівнянь, другий - 10 тисяч операцій.

Інструменти:

  • keyBy('id') - один елемент на ключ;
  • groupBy('customer_id') - кілька елементів на ключ (усі замовлення клієнта);
  • mapWithKeys() - довільні пари ключ-значення;
  • pluck('name', 'id') - словник «id => назва» для випадаючих списків.

Але спершу питання: чи потрібно зіставляти в PHP? Якщо обидва списки з однієї бази, зв'язок з with() чи join зробить це ефективніше й без завантаження зайвого. keyBy() - для даних з різних джерел: API, файл імпорту, інша база.

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

Скорочення для частих замикань, які лише викликають метод чи беруть поле елемента.

// звичайно
$users->each(fn (User $user) => $user->markAsVip());
$total = $orders->sum(fn (Order $order) => $order->total);
$active = $users->filter(fn (User $user) => $user->isActive());

// повідомленнями вищого порядку
$users->each->markAsVip();
$total = $orders->sum->total;
$active = $users->filter->isActive();

Працює для each, map, filter, reject, sum, avg, max, min, sortBy, groupBy, keyBy, first, contains, every, partition, unique та інших.

Коли доречно: коротка операція над кожним елементом, і читається як речення.

Коли ні:

  • потрібні аргументи чи умова - звичайне замикання зрозуміліше;
  • статичний аналіз і IDE гірше розуміють ->each->method(), тож у кодовій базі з суворим PHPStan частина команд їх уникає.

Поряд - макроси: якщо одну й ту саму операцію пишуть у багатьох місцях, її оформлюють як Collection::macro('toCsv', ...) у провайдері чи як метод власної колекції моделі.

Докладніше в документації: Повідомлення вищого порядку