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) 

 [ ![Laravel AI SDK v0.10: 4 New Features Explained](https://cdn.msaied.com/540/e74363f048913d3782bc16a7292215db.png) Laravel AI SDK AI Agents Filesystem Tools 

### Laravel AI SDK v0.10: 4 New Features Explained

Laravel AI SDK v0.10 ships with filesystem tools for agents, human tool approval, and more. This walkthrough c...

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

 12 Aug 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-ai-sdk-v010-4-new-features-explained) [ ![NightOwl: Laravel Monitoring With Flat Pricing and Your Own PostgreSQL Storage](https://cdn.msaied.com/538/60582948c0339b19e872f4f3c03171f2.png) Laravel Monitoring PostgreSQL 

### NightOwl: Laravel Monitoring With Flat Pricing and Your Own PostgreSQL Storage

NightOwl redirects Laravel Nightwatch telemetry into a PostgreSQL database you own, replacing per-event billin...

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

 11 Aug 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/nightowl-laravel-monitoring-with-flat-pricing-and-your-own-postgresql-storage) [ ![Mock PHP Classes in Tests With the Double Library](https://cdn.msaied.com/537/be45e68dffd72aa9bd833c2f409fcb07.png) testing mocking phpunit 

### Mock PHP Classes in Tests With the Double Library

Double is a PHP 8.3 test double library by Jason McCreary that replaces mocks, spies, and partials with a sing...

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

 11 Aug 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/mock-php-classes-in-tests-with-the-double-library) 

   [  ![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)
