Eager loading complex functions with Laravel custom collections
Matthew Erwin • January 23, 2021
laravel performanceIn Laravel, we can eager load relationships to prevent n+1 queries being performed when fetching multiple records from the database.
An example taken from the Laravel docs.
$books = Book::with('author')->get();
foreach ($books as $book) {
echo $book->author->name;
}
This works great for a relatively simple relationship however we can also replicate this for a complex function using custom collections.
This would be useful if your function:
- Fetches information from an API where you could fetch in bulk
- Has lots of complex logic/queries where it would be more efficient to do a single query to fetch in bulk
Take for example this controller to fetch invoices from an external API for every order.
class OrderController {
public function index()
{
$orders = Order::all();
$orders->transform(function(Order $order){
$order->invoice_info = Http::get('http://example.com/invoice', [
'invoice' => $order->id,
]);
return $order;
});
}
}
Lets create a custom collection for our model:
class Order extends Model {
public function newCollection(array $models = [])
{
return new OrderCollection($models);
}
}
class OrderCollection extends Collection {
public function fetchInvoices()
{
$invoices = Http::get('http://example.com/invoice', [
'invoice' => $this->pluck('id')->toArray(),
]);
$this->transform(function(Order $order) use ($invoices){
$order->invoice_info = $invoices[$order->id] ?? null;
return $order;
});
}
}
class OrderController {
public function index()
{
$orders = Order::all()->fetchInvoices();
}
}
This will give the same result however we will only have done 1 API call rather than doing 1 API call for every order.