$ lexprog.com

// notes from an old coder -- php, databases, and the occasional rant

[June 26, 2024] Eloquent ORM

Eloquent New Model Query: Fresh Builder

Eloquent New Model Query: Fresh Builder

────────────────────────────────────────────────────────

Eloquent New Model Query: Fresh Builder

Tip: newQuery() for Fresh Builder

$query = (new Post)->newQuery();

Starts a clean query builder without global scopes applied.

Gotcha: newQuery() Includes Global Scopes

Global scopes are still applied. Use newQueryWithoutScopes() to bypass all scopes.

Tip: resolveRouteBinding()

public function resolveRouteBinding($value, $field = null): ?Model
{
    return $this->where($field ?? 'id', $value)->first();
}

Customize how route model binding resolves the model.

Gotcha: Static Methods Share State

Post::where('published', true);
Post::where('active', true); // Still includes published filter!

Always start with a fresh call: Post::query()->where(...).

Tip: newModelQuery() vs newQuery()

newModelQuery() returns a builder for the model class. newQuery() includes the model's current state.

Gotcha: Query Builder Immutability

Query builders are NOT immutable. Calling where() modifies the builder in place.

Tip: Use cursor() for Memory-Neutral Iteration

When exporting 100K rows, get() loads everything into memory. cursor() uses yield and keeps memory flat regardless of row count. Perfect for artisan commands.

Tip: whereHas() vs load() — Two Different Things

whereHas() filters the parent query by relationship existence. load() eager-loads relationships AFTER the query. Mixing them up is a common source of logic bugs.

Gotcha: withCount() Adds a Subquery

withCount('comments') runs a correlated subquery on every row. On large tables, this can be slower than a separate query. Profile before relying on it.

Senior Insight

Getting a fresh query builder instance from a model via newQuery() or on() is a pattern I use for cross-database operations. But there's a subtle issue: newQuery() inherits global scopes, which can produce unexpected results when you're querying against a different database connection. I use withoutGlobalScopes() explicitly when I need an unmodified query, and I always test cross-database queries with the actual database connection, not SQLite.

Source: Laravel Docs (https://laravel.com/docs/eloquent), Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/eloquent)

────────────────────────────────────────────────────────
<-- back to posts