Measuring finds N+1 afterward; Model::preventLazyLoading() stops it at the line that causes it. Put Model::preventLazyLoading(! $this->app->isProduction()); in AppServiceProvider::boot() so development and tests throw while production keeps working:
use App\Models\Order;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\LazyLoadingViolationException;
Model::preventLazyLoading();
echo 'one model: ', Order::find(3)->customer->name, "\n";
try {
Order::limit(3)->get()->each(fn ($order) => $order->customer->name);
} catch (LazyLoadingViolationException $e) {
echo $e::class, "\n", $e->getMessage(), "\n";
}one model: Chen Wei Illuminate\Database\LazyLoadingViolationException Attempted to lazy load [customer] on model [App\Models\Order] but lazy loading is disabled.
find() did not throw: the builder arms the check only when a query returns more than one row. Automatic eager loading runs before the check, so the two combine safely. To log instead of throwing, register Model::handleLazyLoadingViolationUsing(fn ($model, $relation) => Log::warning(...)); here it wrote one warning per order and the queries still ran. Model::shouldBeStrict() adds preventSilentlyDiscardingAttributes() and preventAccessingMissingAttributes() to the check.