Laravel Local Scope Eloquent ORM Method

Laravel development tips and tricks

Welcome to a fun and insightful journey into one of Laravel’s powerful features—local scopes in the Eloquent ORM! Whether you’re just getting started or looking to refine your Laravel skills, this article will walk you through how to streamline your database queries with local scopes. So, grab a cup of coffee ☕, and let’s get started!

What Are Local Scopes?

Local scopes allow you to define common sets of query that you can easily re-use in your Eloquent models. For example, when you want to find use with some role and you don’t want to repeat yourself, local scopes can come really handy.

Using local scopes with relationships in Laravel allows you to create clean, reusable, and expressive query logic, making your codebase easier to maintain and understand.

Defining a Local Scope

Defining a local scope is as easy as pie 🥧! To define a local scope, add a method to your model and prefix with scope. This method should take a Builder instance as its first parameter, and should return the modified query builder.

For example, let’s say you have a User model and you want to create a local scope that filters users who are active:

        
 <?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model; 

class User extends Model
{   
   public function scopeActive(Builder $query): void
  {
       $query->where('active', 'Y');
  }
}
?>
        
    

Using a Local Scope

Once you have defined the local scope, you can call it directly on your model’s query builder. You don’t need to include the scope prefix when calling the scope method.

For example, you can retrieve all active users like this:

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

You can also chain the local scope with other query constraints:

$activeUsers = User::active()->where(‘role’, ‘admin’)->get();

This makes your code not only shorter but also much easier to read and understand.

Combining Multiple Local Scopes

You can easily combine multiple local scopes in a single query. For instance, if you have another scope for filtering users by role, you can chain them together:

        
 class User extends Model
{
    // Active scope
    public function scopeActive(Builder $query)
    {
        return $query->where('active', 'Y');
    }

    // Role scope
    public function scopeRole(Builder $query, $role)
    {
        return $query->where('role', $role);
    }
}

// Using both scopes
$activeAdmins = User::active()->role('admin')->get();
        
    

Dynamic Scopes

If you need more dynamic scoping, you can pass parameters to your local scope methods. For example, you might want to create a scope that filters users based on their registration date:

        
 class User extends Model
{
    /**
     * Scope a query to only include users registered after a given date.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @param  string  $date
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeRegisteredAfter(Builder $query, $date)
    {
        return $query->where('created_at', '>', $date);
    }
}

// Using the dynamic scope
$recentUsers = User::registeredAfter('2024-08-11')->get();
        
    

Defining a Local Scope in a Related Model

First, let’s define a local scope in a related model. Suppose you have two models: Post and Comment. Each post can have many comments, and each comment can be approved or not.

Comment Model with a Local Scope:

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

class Comment extends Model
{
    /**
     * Scope a query to only include approved comments.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeApproved(Builder $query)
    {
        return $query->where('is_approved', true);
    }
}        
    

Using Scopes with Relationships

Now, you can use this scope when querying the Post model to get posts with approved comments.

Post Model:

        
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    /**
     * Get the comments for the post.
     */
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}       
    

Applying the Scope in a Relationship Query

You can apply the approved scope when retrieving posts with their approved comments.

        
$posts = Post::with(['comments' => function ($query) {
    $query->approved();
}])->get();     
    

In this example, the with method is used to eager load the comments relationship. The closure allows you to apply additional query constraints to the related comments model, such as the approved scope.

Combining Multiple Scopes and Constraints

You can combine multiple scopes and additional constraints within the relationship query. For instance, if you want to get posts with approved comments created after a certain date, you can do the following:

        
$posts = Post::with(['comments' => function ($query) {
    $query->approved()->where('created_at', '>', now()->subDays(30));
}])->get(); 
    

Filtering Parent Models Based on Related Models’ Scopes

Sometimes, you might want to filter parent models based on the conditions of related models. For example, you might want to get only posts that have approved comments.

You can achieve this using whereHas:

        
$postsWithApprovedComments = Post::whereHas('comments', function ($query) {
    $query->approved();
})->get(); 
    

In this case, whereHas adds a constraint to the Post query to only include posts that have at least one approved comment.

Example Scenario

Here’s a complete example combining all the elements:

Models:

        
class Post extends Model
{
    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}

class Comment extends Model
{
    public function scopeApproved(Builder $query)
    {
        return $query->where('is_approved', true);
    }
}

    

Controller or Query Logic:

        
// Get posts with their approved comments
$posts = Post::with(['comments' => function ($query) {
    $query->approved();
}])->get();

// Get only posts that have approved comments
$postsWithApprovedComments = Post::whereHas('comments', function ($query) {
    $query->approved();
})->get();

    

Wrapping It All Up 🎁

So, there you have it, folks! Local scopes in Laravel’s Eloquent ORM are your ticket to writing cleaner, more maintainable code. Whether you’re combining scopes, applying them to relationships, or just keeping your queries DRY (Don’t Repeat Yourself), these little tools can make a big difference.

If you haven’t already, give local scopes a try in your next Laravel project. Trust me, once you start using them, you’ll wonder how you ever lived without them!

Got questions? Want to see more examples? If yes, then we’re just a call away. Contact us now!

And hey, if you found this guide helpful, why not share it with your fellow Laravel enthusiasts? Let’s spread the knowledge and make coding just a little bit easier for everyone. Happy coding!