Backend Development 13 min read

Understanding and Implementing Laravel Scopes for Eloquent Models

Laravel Scopes provide a powerful mechanism to define reusable query constraints for Eloquent models, covering global, local, anonymous, and dynamic scopes, with step‑by‑step instructions on creation, application, removal, and best practices, enhancing code reuse, readability, and maintainability in backend development.

php中文网 Courses
php中文网 Courses
php中文网 Courses
Understanding and Implementing Laravel Scopes for Eloquent Models

Laravel is a popular PHP framework known for its rich feature set that can significantly boost web development productivity. Laravel Scopes are a practical feature that allows you to define reusable and chainable query constraints for Eloquent models. This article dives deep into the concept of Laravel Scopes and guides you through their implementation in a Laravel project.

What is a Laravel Scope?

A Laravel scope is a mechanism that encapsulates query constraints into reusable, easy‑to‑use methods. It offers a convenient way to apply query conditions, reducing code duplication and improving code organization clarity.

Types of Laravel Scopes

Laravel scopes come in two varieties: Global Scope and Local Scope .

Global Scope

A global scope is defined inside a model and is automatically applied to all queries for that model. It is useful when you want to enforce a condition (e.g., only retrieve active users or only published posts) without having to add the condition manually to each query.

Generating a Scope

You can create a new global scope using the php artisan make:scope command, which places the generated class in the app/Models/Scopes directory:

<code>php artisan make:scope ActiveUserScope</code>

Creating a global scope is a straightforward process:

1. Use the make:scope command to generate a class that implements the Illuminate\Database\Eloquent\Scope interface.

2. Implement the apply method in the generated class.

3. Inside apply , add the desired where constraints or other clauses to build the query condition.

<code>&lt;?php
namespace App\Models\Scopes;

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

class ActiveUserScope implements Scope
{
    /**
     * Apply the scope to a given Eloquent query builder.
     */
    public function apply(Builder $builder, Model $model): void
    {
        $builder->where(column: 'is_active', operator: true);
    }
}
</code>

Applying a Global Scope

To apply a global scope to a model, follow these steps:

Override the model's booted method.

Within booted , call the model's addGlobalScope method.

Pass an instance of the scope class as the sole argument to addGlobalScope .

<code>&lt;?php
namespace App\Models;

use App\Models\Scopes\ActiveUserScope;

class User extends Model
{
    protected static function booted()
    {
        static::addGlobalScope(new ActiveUserScope());
    }
}
</code>

After adding the scope to the App\Models\User model, calling methods such as all() , get() , or first() will execute the following SQL query:

<code>select * from `users` where `is_active` = true</code>

Anonymous Global Scopes

Eloquent also allows you to define global scopes using closures, which is handy for simple scopes that do not require a dedicated class.

1. Provide a custom scope name as the first argument to addGlobalScope . 2. Inside the closure, define the scope logic, typically adding a where constraint or other clause.

<code>/**
 * The "booted" method of the model.
 * @return void
 */
protected static function booted(): void
{
    static::addGlobalScope(scope: 'activeUser', function (Builder $builder) {
        $builder->where(column: 'is_active', operator: true);
    });
}
</code>

Removing Global Scopes

To remove a global scope from a specific query, use the withoutGlobalScope method, passing the scope class name as its sole argument:

<code>User::withoutGlobalScope(ActiveUserScope::class)->get();</code>

If the scope was defined via a closure, provide the string name assigned to the scope:

<code>User::withoutGlobalScope('activeUser')->get();</code>

To remove one or more global scopes, you can use withoutGlobalScopes :

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

// Remove specific scopes
User::withoutGlobalScopes([
    ActiveUserScope::class,
    HasEmailVerifiedScope::class
])->get();
</code>

Local Scopes

Local scopes are defined as methods within a model and are applied each time the method is invoked. They are useful for dynamically applying conditions based on user input or other factors without polluting controller or service code.

A scope should always return the same query builder instance (or void):

<code>&lt;?php
namespace App\Models;

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

class User extends Model
{
    /**
     * @param Builder $builder
     * @return void
     */
    public function scopeEmailVerified(Builder $builder): void
    {
        $builder->whereNotNull('email_verified_at');
    }

    /**
     * @param Builder $builder
     * @return void
     */
    public function scopeActiveUser(Builder $builder): void
    {
        $builder->where('is_active', true);
    }
}
</code>

Using Local Scopes

After defining a scope, you can call it on the model without the scope prefix and chain multiple scopes together:

<code>use App\Models\User;

$users = User::emailVerified()->activeUser()->get();
</code>

The resulting SQL is:

<code>select * from `users` where `email_verified_at` is not null and `is_active` = true</code>

To combine scopes with an or operator, you may need a closure to group the logic correctly:

<code>User::emailVerified()->orWhere(function (Builder $query) {
    $query->activeUser();
})->get();
</code>

SQL produced:

<code>select * from `users` where (`email_verified_at` is not null or (`is_active` = true));
</code>

Laravel also offers a higher‑order orWhere method that allows fluent chaining without a closure:

<code>User::emailVerified()->orWhere->activeUser();
</code>

Dynamic Scopes

Sometimes you need a scope that accepts parameters. Simply add additional parameters after the $query argument in the scope method signature:

<code>/**
 * @param Builder $query
 * @param string $role
 * @return void
 */
public function scopeOfRole(Builder $query, string $role): void
{
    $query->where('role', $role);
}
</code>

Usage example:

<code>$users = User::ofRole('admin')->get();
</code>

Implementing Laravel Scopes

When many scopes are needed, you can group them in a Trait and let the model use that trait:

<code>use App\Models\User\UserScopeHelper;

class User extends Model
{
    use UserScopeHelper;
    /** Your code **/
}
</code>
<code>&lt;?php
declare(strict_types=1);

namespace App\Models\Traits;

use Illuminate\Database\Eloquent\Builder;

trait UserScopeHelper
{
    /**
     * Query only active users
     */
    public function scopeActiveUser(Builder $builder): void
    {
        $builder->where('is_active', true);
    }

    /**
     * Query only users with verified email
     */
    public function scopeEmailVerified(Builder $builder): void
    {
        $builder->whereNotNull('email_verified_at');
    }

    /**
     * Query users by role
     */
    public function scopeOfRole(Builder $builder, string $role): void
    {
        $builder->where('role', $role);
    }
}
</code>

Apply the trait in the model:

<code>&lt;?php
namespace App\Models;

use App\Models\Traits\UserScopeHelper;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    use UserScopeHelper;
}
</code>

Advantages of Laravel Scopes

1. Code Reusability: Scopes let you define a query constraint once and reuse it throughout the application, eliminating duplication and promoting cleaner, maintainable code.

2. Improved Readability: By encapsulating constraints in named methods, scopes make code more expressive and easier to understand, reducing noise from repeated conditions.

3. Flexibility: Laravel scopes provide the flexibility to dynamically add conditions to queries. Local scopes enable easy chaining of multiple scope methods, adapting to evolving requirements.

Conclusion

Laravel Scopes offer a powerful and elegant way to define reusable query constraints for Eloquent models. By encapsulating query logic within scopes, developers achieve cleaner code, enhanced reusability, and improved maintainability of Laravel applications. Their ease of implementation and flexibility make scopes an invaluable tool for any Laravel developer.

PHPLaravelEloquentQueryScopestrait
php中文网 Courses
Written by

php中文网 Courses

php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.