Skip to content

Ultimate Guide to Laravel Scopes: Local, Global, Dynamic & Best Practices

Learn everything about Laravel Scopes in this complete guide. Explore local, global, and dynamic scopes with practical code examples, best practices, and tips to write cleaner, reusable, and efficient queries in your Laravel 12 projects.

Dasun Kalupahana 2 min read 1.5K views 0 comments

Scopes in Laravel are a powerful feature that allows you to encapsulate query constraints into reusable methods. They help keep your code DRY (Don’t Repeat Yourself) and make your queries more readable.

Table of Contents

  1. Types of Scopes
  2. Local Scopes
  3. Global Scopes
  4. Dynamic Scopes
  5. Scope Features
  6. Best Practices

1. Types of Scopes

There are two main types of scopes in Laravel:

  1. Local Scopes: Apply to specific queries when called.
  2. Global Scopes: Apply to all queries for a model.

2. Local Scopes

Local scopes allow you to define common sets of query constraints that you may easily re-use.

Basic Example

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Attributes\Scope;

class User extends Model
{
    /**
     * Scope a query to only include active users.
     */
    #[Scope]
    public function active(Builder $query)
    {
        return $query->where('active', 1);
    }
    
    /**
     * Scope a query to only include popular users.
     */
    #[Scope]
    public function popular(Builder $query)
    {
        return $query->where('votes', '>', 10);
    }
}

Using Local Scopes

// Get all active users
$activeUsers = User::active()->get();

// Get all popular users
$popularUsers = User::popular()->get();

// Chain multiple scopes
$users = User::active()->popular()->orderBy('name')->get();

/**
* Combining multiple Eloquent model scopes via an or query operator 
* may require the use of closures to achieve the correct logical grouping:
*/
$users = User::active()->orWhere(function (Builder $query) {
    $query->popular();
})->get();

/**
* Laravel offers a higher-order orWhere method that enables 
* fluent scope chaining without requiring closure wrappers.
*/
$users = User::active()->orWhere->popular()->get();

3. Global Scopes

Global scopes allow you to add constraints to all queries for a given model.

Creating a Global Scope:

You can generate a new global scope using the Artisan command php artisan make:scope ScopeName. This will automatically create the scope class in your application's app/Models/Scopes directory.

php artisan make:scope AgeScope

The apply method can add WHERE clauses or other query constraints to modify the query builder instance:

<?php

namespace App\Models\Scopes;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class AgeScope implements Scope
{
    /**
     * Apply the scope to a given Eloquent query builder.
     */
    public function apply(Builder $builder, Model $model): void
    {
        $builder->where('age', '>', 18);
    }
}

Apply the scope to a model:

<?php

namespace App\Models;

use App\Models\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Attributes\ScopedBy;

#[ScopedBy([AgeScope::class])]
class User extends Model
{
    //
}

Alternatively, you can manually register a global scope by overriding the model’s booted() method and calling the addGlobalScope() method:

<?php

namespace App\Models;

use App\Models\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The "booted" method of the model.
     */
    protected static function booted(): void
    {
        static::addGlobalScope(new AgeScope);
    }
}

Now all queries for the User model will automatically include the age constraint.

Anonymous Global Scopes

You can also define global scopes using closures:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The "booted" method of the model.
     */
    protected static function booted(): void
    {
        static::addGlobalScope('age', function (Builder $builder) {
            $builder->where('age', '>', 18);
        });
    }
}

Removing Global Scopes

/** To remove a global scope for a specific query, 
*   use the withoutGlobalScope() method:
*/
User::withoutGlobalScope(AgeScope::class)->get();

// Alternatively, if you defined the global scope using a closure
User::withoutGlobalScope('age')->get();

// Remove all global scopes
User::withoutGlobalScopes()->get();

// Remove specific global scopes
User::withoutGlobalScopes([
    AgeScope::class
])->get();

4. Dynamic Scopes

You can create scopes that accept parameters:

class User extends Model
{
    /**
     * Scope a query to only include users of a given type.
     */
    public function scopeOfType(Builder $query, string $type): void
    {
        $query->where('type', $type);
    }
}
// Usage
$admins = User::ofType('admin')->get();

5. Scope Features

Chaining Scopes

$users = User::active()
            ->ofType('admin')
            ->orderBy('name')
            ->get();

Combining with Other Query Builder Methods

$users = User::active()
            ->where('created_at', '>', now()->subDays(30))
            ->with('posts')
            ->paginate(10);

Conditional Scopes

#[Scope]
public function activeOrAdmin($query, $isAdmin)
{
    return $query->when(!$isAdmin, function ($query) {
        return $query->active();
    });
}

6. Best Practices

  • Keep scopes focused: Each scope should do one specific thing
  • Document your scopes: Use PHPDoc to explain what each scope does
  • Consider performance: Complex scopes can impact query performance
  • Use dynamic scopes judiciously: They’re powerful but can make code harder to understand if overused

Scopes are a powerful tool in Laravel that can significantly improve the organization and readability of your database queries while reducing code duplication.

Free resources for this topic

Code snippets and fixes for this topic

Get one practical tip every week

New tutorials, fixes and free cheat sheets from DEV Talk, straight to your inbox. No spam, unsubscribe in one click.

Free download: Laravel interview questions (PDF)

Have a question about this post?

Ask the community: other developers and the author can answer.

Ask the community

Get notified about new articles

One click, no email needed. Turn it off anytime with the bell at the top.

Get new articles on your iPhone

Add DEV Talk to your Home Screen, open it, then tap the bell.

Rate this post
0.0
Share

Enjoying the content?

If this article helped you, you can support the author with a coffee. It keeps new posts coming.

Buy me a coffee (opens in a new tab)

Comments 0

Be kind. Comments with links are reviewed before they appear.

No comments yet

Be the first to share your thoughts.

Get new articles in your inbox

Practical tutorials and deep dives, about once a week. No spam, unsubscribe anytime.

Privacy policy

Add DEV Talk to your Home Screen

  1. 1 Tap the Share button in Safari's toolbar.
  2. 2 Scroll down and tap Add to Home Screen.
  3. 3 Tap Add. Open it from the Home Screen to read like an app and to turn on notifications with the bell.

We use cookies to improve your experience and analyze traffic. By continuing, you agree to our Privacy Policy.