Eloquent Query Scopes: Global, Local &amp; Pending Internals | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Eloquent Query Scopes at Scale: Global, Local, and Pending Scope Internals        On this page       1. [  Eloquent Query Scopes at Scale ](#eloquent-query-scopes-at-scale)
2. [  How Eloquent Applies Scopes Internally ](#how-eloquent-applies-scopes-internally)
3. [  Global Scope Pitfall: Ambiguous Columns on Joins ](#global-scope-pitfall-ambiguous-columns-on-joins)
4. [  Local Scopes: Composable by Design ](#local-scopes-composable-by-design)
5. [  The withoutGlobalScope Escape Hatch ](#the-codewithoutglobalscopecode-escape-hatch)
6. [  Pending Scopes and Eager Loading ](#pending-scopes-and-eager-loading)
7. [  Takeaways ](#takeaways)

  ![Eloquent Query Scopes at Scale: Global, Local, and Pending Scope Internals](https://cdn.msaied.com/278/4614d18b4735498f765593b468cbe591.png)

  #laravel   #eloquent   #database   #architecture  

 Eloquent Query Scopes at Scale: Global, Local, and Pending Scope Internals 
============================================================================

     24 Jun 2026      2 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Eloquent Query Scopes at Scale  ](#eloquent-query-scopes-at-scale)
2. [  02   How Eloquent Applies Scopes Internally  ](#how-eloquent-applies-scopes-internally)
3. [  03   Global Scope Pitfall: Ambiguous Columns on Joins  ](#global-scope-pitfall-ambiguous-columns-on-joins)
4. [  04   Local Scopes: Composable by Design  ](#local-scopes-composable-by-design)
5. [  05   The withoutGlobalScope Escape Hatch  ](#the-codewithoutglobalscopecode-escape-hatch)
6. [  06   Pending Scopes and Eager Loading  ](#pending-scopes-and-eager-loading)
7. [  07   Takeaways  ](#takeaways)

 Eloquent Query Scopes at Scale
------------------------------

Scopes feel simple until they silently corrupt a join, double-apply a `where`, or make a multi-tenant query leak rows. Understanding what happens inside `Builder::applyScopes()` changes how you design them.

### How Eloquent Applies Scopes Internally

Every `Model::query()` call returns an `Illuminate\Database\Eloquent\Builder` wrapping a base `QueryBuilder`. Global scopes are stored on the model class and applied lazily — not when you call `query()`, but when the builder is about to compile SQL. The entry point is `Builder::applyScopes()`:

```php
// Simplified from Illuminate\Database\Eloquent\Builder
public function applyScopes(): static
{
    foreach ($this->model->getGlobalScopes() as $identifier => $scope) {
        if (! isset($this->removedScopes[$identifier])) {
            $scope->apply($this, $this->model);
        }
    }
    return $this;
}

```

This lazy evaluation is why you can call `withoutGlobalScope(TenantScope::class)` after `Model::query()` and it still works — the scope hasn't run yet.

### Global Scope Pitfall: Ambiguous Columns on Joins

The classic trap: your `TenantScope` adds `->where('tenant_id', tenant())`. Then a join brings in another table with a `tenant_id` column.

```php
class TenantScope implements Scope
{
    public function apply(Builder $builder, Model $model): void
    {
        // BAD: ambiguous when joined
        $builder->where('tenant_id', tenant()->id);
    }
}

```

PostgreSQL and MySQL both throw `Column 'tenant_id' in where clause is ambiguous`. Fix it by qualifying the column:

```php
public function apply(Builder $builder, Model $model): void
{
    $table = $model->getTable();
    $builder->where("{$table}.tenant_id", tenant()->id);
}

```

Always qualify columns in global scopes. It costs nothing and prevents production incidents.

### Local Scopes: Composable by Design

Local scopes should be narrow and composable, not a grab-bag of business logic. A scope that does too much becomes impossible to reuse.

```php
class Order extends Model
{
    public function scopePending(Builder $query): Builder
    {
        return $query->where('status', OrderStatus::Pending);
    }

    public function scopeOlderThan(Builder $query, Carbon $date): Builder
    {
        return $query->where('created_at', 'for(Customer::factory()->create())
        ->create();

    $loaded = Order::with('customer')->find($order->id);

    expect($loaded->customer)->not->toBeNull();
});

```

### Takeaways

- Global scopes are applied lazily at SQL compile time, so `withoutGlobalScope()` works even after `query()`.
- Always qualify column names in global scopes to prevent ambiguity on joins.
- Keep local scopes narrow and single-purpose; compose them at the call site.
- Eager-loaded relationships get their own builder — global scopes on related models apply independently.
- Every `withoutGlobalScopes()` call deserves a comment; treat it as a deliberate bypass, not a convenience.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Feloquent-query-scopes-at-scale-global-local-and-pending-scope-internals&text=Eloquent+Query+Scopes+at+Scale%3A+Global%2C+Local%2C+and+Pending+Scope+Internals) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Feloquent-query-scopes-at-scale-global-local-and-pending-scope-internals) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  Can a global scope cause problems with Eloquent eager loading?        Not usually — eager loading creates a fresh builder for the related model, so global scopes on that model apply correctly. The risk is when a global scope uses unqualified column names and the eager load involves a join internally, which can cause ambiguity errors. 

      Q02  Is it safe to call withoutGlobalScope() inside a repository method?        Yes, but document it clearly. Because scopes are applied lazily, calling withoutGlobalScope() before the query executes is always safe. The danger is accidentally bypassing tenant isolation — restrict such calls to explicitly privileged service classes and cover them with tests. 

      Q03  Do local scopes work with cursor() and lazy collections?        Yes. cursor() and lazyById() both go through the same Eloquent builder pipeline, so all applied local and global scopes are respected. The difference is in how rows are hydrated — one at a time via a generator — not in how the WHERE clauses are built. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![Typed Enums as First-Class Domain Citizens in Laravel with PHP 8.3](https://cdn.msaied.com/282/71a8fc3e4cf4239b1bf6d38d57e0b985.png) laravel php8.3 enums 

### Typed Enums as First-Class Domain Citizens in Laravel with PHP 8.3

Go beyond simple enum labels. Learn how to attach behaviour, implement interfaces, and use backed enums as Elo...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 24 Jun 2026     3 min read  

  Read    

 ](https://msaied.com/articles/typed-enums-as-first-class-domain-citizens-in-laravel-with-php-83) [ ![RAG in Laravel: pgvector, Embeddings, and Retrieval-Augmented Generation in Practice](https://cdn.msaied.com/281/8d2ac57c0e69d3ff9f1e68faf0e4d10c.png) laravel ai pgvector 

### RAG in Laravel: pgvector, Embeddings, and Retrieval-Augmented Generation in Practice

Build a production-ready RAG pipeline in Laravel using pgvector, OpenAI embeddings, and a clean retrieval laye...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 24 Jun 2026     4 min read  

  Read    

 ](https://msaied.com/articles/rag-in-laravel-pgvector-embeddings-and-retrieval-augmented-generation-in-practice) [ ![Ship AI with Laravel: Failover, Queues, and Middleware for AI Agents](https://cdn.msaied.com/283/f0a6d6a6f22d9131bacb96bae1bfc10b.png) Laravel AI Agents Queues 

### Ship AI with Laravel: Failover, Queues, and Middleware for AI Agents

Learn how to make Laravel AI agents production-ready with automatic provider failover, background queue proces...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 24 Jun 2026     3 min read  

  Read    

 ](https://msaied.com/articles/ship-ai-with-laravel-failover-queues-and-middleware-for-ai-agents) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
