Senior: питання на співбесіді з теми «Колекції»
Питання з реальних співбесід з відповідями: Laravel і PHP, бази даних, JavaScript і фронтенд, Git, Docker, API, безпека й архітектура. Тими самими темами, що й тести.
2 питання
Колекції зручні настільки, що ними легко почати робити роботу бази - і заплатити памʼяттю та часом.
Типова помилка:
$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(): обхід пачками з синтаксисом колекції, коли обробити треба всі рядки, але тримати їх у памʼяті не можна.
Щоб логіка над набором моделей жила в одному місці, а не повторювалася в контролерах і шаблонах.
#[CollectedBy(OrderCollection::class)]
class Order extends Model {}
class OrderCollection extends Collection
{
public function paid(): static
{
return $this->filter(fn (Order $order) => $order->isPaid());
}
public function totalInCents(): int
{
return $this->sum(fn (Order $order) => $order->total_cents);
}
}
$orders = $customer->orders; // OrderCollection
$orders->paid()->totalInCents();
Замість атрибута можна перевизначити newCollection() у моделі.
Де межа з іншими інструментами:
- скоп (
Order::paid()) - фільтрує в базі, коли рядків багато; - метод колекції - коли моделі вже завантажені й потрібно обчислення в пам'яті (кошик, рахунок, сторінка зі списком);
- окремий клас (сервіс, звіт) - коли логіка залежить від зовнішніх сервісів чи її багато.
Переваги: типи - IDE знає, що повертає $customer->orders; тести - методи колекції тестуються без бази на make()-моделях.
Обережно з назвами, що збігаються з методами базової колекції, - перевизначення filter() чи sum() зламає код, який їх очікує.