Automatic Eager Loading

Laravel 12.8.0 2,157 (8 April 2025) added Model::automaticallyEagerLoadRelationships(), called in AppServiceProvider::boot(), and withRelationshipAutoloading() for one collection. Reading a missing relationship on one model then lazy eager loads it for the whole collection, and the loaded models inherit the behavior. The 12.x documentation labeled this beta; the 13.x documentation carries no such warning, and both methods exist in 13.33.0.

Three levels deep: lazy, then both forms of autoloadingSQL
use App\Models\Customer;
use Illuminate\Database\Eloquent\Model;
$report = fn ($customers) => $customers->each(fn ($customer) => $customer->orders
    ->each(fn ($order) => $order->products->each(fn ($product) => $product->category->name)));
measure('lazy', fn () => $report(Customer::limit(100)->get()));
measure('collection', fn () => $report(Customer::limit(100)->get()
    ->withRelationshipAutoloading()));
Model::automaticallyEagerLoadRelationships();
measure('global', fn () => $report(Customer::limit(100)->get()));
measure('global+()', fn () => Customer::limit(100)->get()
    ->each(fn ($customer) => $customer->orders()->count()));
Output
lazy         1507 queries   885.0 ms in MySQL  1283.6 ms in all
collection      4 queries     8.8 ms in MySQL    64.8 ms in all
global          4 queries     9.1 ms in MySQL    65.3 ms in all
global+()     101 queries    52.1 ms in MySQL    67.3 ms in all

Lazily the report costs 1 + 100 + 404 + 1,002 queries (customers, orders, each order's products, each product's category); autoloading needs the same four as with('orders.products.category'). It hooks property access only, so orders()->count() still runs once per customer, and it cannot select columns or constrain. Treat it as a safety net and keep with() where you know the shape.