Master Laravel Query Scopes: Optimize Your Database Queries

Abu Sayed
3 min readJul 13, 2024

--

Learn how to use Laravel Query Scopes to write cleaner, more efficient database queries. Explore local and global scopes, best practices, and testing techniques.

Mastering Query Scopes in Laravel: Streamline Your Database Queries

As Laravel developers, we often find ourselves writing repetitive database queries across our applications. Whether it’s filtering active users, retrieving published posts, or limiting results to a specific team in a multi-tenant app, these patterns can clutter our code and make maintenance a headache. Enter Query Scopes — a powerful Laravel feature that allows us to encapsulate and reuse query logic effortlessly. In this comprehensive guide, we’ll dive deep into Query Scopes, exploring how they can revolutionize your Laravel development process.

Understanding Query Scopes

Query Scopes in Laravel are a way to define commonly used sets of database query constraints. They allow you to encapsulate query logic within your Eloquent models, making your code more readable, maintainable, and DRY (Don’t Repeat Yourself).

There are two types of Query Scopes in Laravel: a) Local Scopes b) Global Scopes

Let’s explore each type in detail.

Local Query Scopes

Local scopes allow you to define reusable query constraints that you can then chain onto any query. They are perfect for commonly used filters or conditions that you want to apply across multiple queries.

How to Define a Local Scope: To create a local scope, define a method on your Eloquent model with the prefix “scope”. For example:

class User extends Model
{
public function scopeActive($query)
{
return $query->where('status', 'active');
}
}

Using a Local Scope: Once defined, you can use the scope in your queries like this:

$activeUsers = User::active()->get();

This is equivalent to writing:

$activeUsers = User::where('status', 'active')->get();

But it’s much more readable and reusable!

Global Query Scopes

Global scopes allow you to add constraints to all queries for a given model. They are automatically applied every time you query the model.

How to Define a Global Scope: There are two ways to define a global scope:

a) Using a closure:

class User extends Model
{
protected static function booted()
{
static::addGlobalScope('active', function (Builder $builder) {
$builder->where('status', 'active');
});
}
}

b) Using a dedicated class:

class ActiveScope implements Scope
{
public function apply(Builder $builder, Model $model)
{
$builder->where('status', 'active');
}
}
class User extends Model
{
protected static function booted()
{
static::addGlobalScope(new ActiveScope);
}
}

With global scopes, every query on the User model will automatically include the ‘active’ constraint.

Removing Global Scopes

There might be situations where you want to query a model without a global scope. Laravel provides methods to remove global scopes:

// Remove all global scopes
User::withoutGlobalScopes()->get();
// Remove specific global scopes
User::withoutGlobalScope(ActiveScope::class)->get();

Best Practices for Using Query Scopes

a) Keep It Simple

Scopes should encapsulate simple, reusable query logic. If a scope becomes too complex, consider breaking it down or using a query builder.

b) Naming Conventions

Use clear, descriptive names for your scopes. For example, scopePublished() is better than scopeP().

c) Composing Scopes

You can compose multiple scopes together to create more complex queries:

$featuredActiveUsers = User::active()->featured()->get();

d) Avoid Overuse

While scopes are powerful, not every query needs to be a scope. Use them for truly reusable logic.

Testing Query Scopes

Testing your query scopes is crucial to ensure they behave as expected. Here’s an example of how you might test a local scope:

public function testActiveScope()
{
User::factory()->count(3)->create(['status' => 'active']);
User::factory()->count(2)->create(['status' => 'inactive']);
    $activeUsers = User::active()->get();    $this->assertCount(3, $activeUsers);
$this->assertTrue($activeUsers->every(fn ($user) => $user->status === 'active'));
}

Conclusion

Query Scopes in Laravel offer a powerful way to clean up your database queries, improve code readability, and enforce consistency across your application. By mastering both local and global scopes, you’ll be able to write more efficient, maintainable Laravel applications. Remember to use scopes judiciously, test them thoroughly, and leverage their composability to create expressive, powerful queries. Happy coding!

--

--

Abu Sayed

Bangladeshi Full Stack Web Dev, Sys Admin & DevOps Engineer. Skills: Data analysis, SQL, Kubernetes. Python, PHP & Laravel. Me on: bd.linkedin.com/in/imabusayed