Eloquent with() is the fix for the N+1 you just wrote

A relation read inside a loop issues a query per iteration. Fifty orders rendered with the customer name attached is fifty-one queries, and nothing on the page says so — the property access reads exactly like an array lookup, which is the whole point of the abstraction and also the problem with it.

// 1 + 50 queries
$orders = Order::where('status', 'paid')->take(50)->get();

foreach ($orders as $order) {
    echo $order->customer->name;
}

// 2 queries: the orders, then one IN () for the customers
$orders = Order::with('customer')->where('status', 'paid')->take(50)->get();

// nested, and constrained
$orders = Order::with(array(
    'customer.company',
    'items' => function ($query) {
        $query->where('refunded', 0);
    },
))->get();

Eager loading collects the foreign keys out of the first result set and fetches the related rows in one WHERE ... IN (...), so the query count stops scaling with the page size. DB::getQueryLog() at the end of the request is how you confirm that rather than assume it. The cost is symmetrical: with() loads the relation for every parent, so on a page where two rows out of fifty are ever expanded it fetches forty-eight relations nobody reads — which is the honest case for leaving a lazy load alone. It also cannot help a relation that needs different constraints per row, because the closure applies to the whole batch.