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

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

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

  [ home ](https://www.msaied.com)    [ articles ](https://www.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%2Fwww.msaied.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%2Fwww.msaied.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://www.msaied.com/articles) 

 [ ![Read/Write Splitting, Connection Pooling, and Sticky Reads in Laravel](https://cdn.msaied.com/295/d977bd189583149245c03d6d763d9db5.png) laravel database performance 

### Read/Write Splitting, Connection Pooling, and Sticky Reads in Laravel

Learn how Laravel's database layer handles read/write splitting, when sticky reads matter, and how to layer Pg...

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

 26 Jun 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/readwrite-splitting-connection-pooling-and-sticky-reads-in-laravel-2) [ ![Laravel Service Container: Contextual Binding, Tagging, and Method Injection](https://cdn.msaied.com/294/e5b9d047bd33c3f8b80069ef6a178884.png) laravel service-container dependency-injection 

### Laravel Service Container: Contextual Binding, Tagging, and Method Injection

Go beyond basic binding. Learn how contextual binding resolves different implementations per consumer, how tag...

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

 26 Jun 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-service-container-contextual-binding-tagging-and-method-injection-1) [ ![PostgreSQL JSONB in Laravel: Indexing, Querying, and Casting Without the Pain](https://cdn.msaied.com/293/f392a5ea52536901eac9677ffa2d070d.png) laravel postgresql eloquent 

### PostgreSQL JSONB in Laravel: Indexing, Querying, and Casting Without the Pain

JSONB columns unlock flexible schemas, but most Laravel apps leave performance on the table. Learn how to inde...

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

 25 Jun 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/postgresql-jsonb-in-laravel-indexing-querying-and-casting-without-the-pain) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.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://www.msaied.com)
- [Projects](https://www.msaied.com/projects)
- [Articles](https://www.msaied.com/articles)
- [Certificates](https://www.msaied.com/certificates)
- [Contact](https://www.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)
