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, методи не матеріалізують увесь набір - обчислення «ліниві» й виконуються лише при ітерації.
Перетворити один зі списків на «словник» за ключем - тоді пошук стає миттєвим.
// 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, файл імпорту, інша база.
Скорочення для частих замикань, які лише викликають метод чи беруть поле елемента.
// звичайно
$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', ...) у провайдері чи як метод власної колекції моделі.