Eager loading complex functions with Laravel custom collections

Matthew Erwin • January 23, 2021

laravel performance

In 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:

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.