Щоб логіка над набором моделей жила в одному місці, а не повторювалася в контролерах і шаблонах.
#[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() зламає код, який їх очікує.