Laravel Eloquent Query Scope with Example

Query Scopes in Laravel allow you to easily re-use query logic in your models. To define a scope, simply prefix a model method with scope. Scopes allow you to add constraints to queries for a given model. Basically it helps on code re-usability and separating query logic and clean controller to some extend. We can say Scopes are methods that can be applied in our Eloquent queries to filter among the data in the database.

Writing Global or Local scopes can provide a convenient and easy way to make sure the query for a given model receives certain constraints. There are two types of Query Scopes in Laravel.

1. Global Query Scopes

2. Local Query Scopes

In this tutorial we will see both types of Eloquent Query Scope with some examples.

1. Global Query Scopes

Global scopes are defined for the whole model. It will scope(add constraints ) all the queries for the given model. One the common example of Global query of Laravel is soft delete functionality.

To write global scope, define a class that implements the Illuminate\Database\Eloquent\Scope interface. It is required to implement apply method. This method may add constrains like where or other type of clauses to the query as per need.

Here I have an example of ActiveScope inside App/Scopes directory:

<?php

namespace App\Scopes;

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

class ActiveScope implements Scope
{
    /**
     * Apply the scope to a given Eloquent query builder.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return void
     */
    public function apply(Builder $builder, Model $model)
    {
        $builder->where(['status','Active']);
    }
} 

Note: Laravel does not have any specific directory pre-defined for Scopes. So you are free to create as many directory as you need.

To assign a global scope to a model, you should override the model's booted method and invoke the model's addGlobalScope method. The addGlobalScope method accepts an instance of your scope as its only argument:

<?php

namespace App\Models;

use App\Scopes\ActiveScope;
use Illuminate\Database\Eloquent\Model;

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

As we have override our user model with ActiveScope every time we make queries our this scope will constrained each time. For example if we run $users = User::all(); we will get result of$users = User::where('status', 'Active')->get(); .

To make query without the global scope you may use the withoutGlobalScope method as follows:

User::withoutGlobalScope(ActiveScope::class)->get(); 

Or

User::withoutGlobalScope('active')->get(); 

Or

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

// Remove some of the global scopes
User::withoutGlobalScopes([
    FirstScope::class, SecondScope::class
])->get(); 

2. Local Scope

Local scopes allow you to define common sets of query constraints that you may easily re-use throughout your application. For example, you may need to frequently retrieve all post that are considered "popular". To define a scope, prefix an Eloquent model method with scope. Consider we have a post model here.

Here, we will add popular scope in our post model. So when we query in controller then we will use that scope..

app/Post.php

namespace App;
  
use Illuminate\Database\Eloquent\Model;
  
class Post extends Model
{
    public $table = "posts";
      
    protected $fillable = [
        'id', 'title', 'body', 'status','votes','views', 'type','category'
    ];
  
    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100)->where('views','>',100);
    }
} 

To use/implement the scope in our query we can simply call the scope without the scope prefix. lets see the example below:

$popular_posts = Post::popular()->orderBy('created_at')->get() 

The above query will give us all the post that have both views and votes greater than 100.

Dynamic Scopes

Some times we are taking user inputs to filter our data, in such cases Dynamic Scopes comes in handy. For this we can add another parameter in our scope method's signature. And needs to parameter from where it is called. Other parameters should be added after $query`.

Lets see again our post model:

namespace App;
  
use Illuminate\Database\Eloquent\Model;
  
class Post extends Model
{
    public $table = "posts";
      
    protected $fillable = [
        'id', 'title', 'body', 'status','votes','views', 'subscription','category'
    ];
  
    public function scopeOfCategory($query,$category,)
    {
        return $query->where('category',$category);
    }
    
     public function scopeSubscriptionType($query,$category,)
    {
        return $query->where('subscription',$type);
    }
} 

To implement **Dynamic Scopes ** you need to pass the argument while making a call to the scope method as follows:

$posts = Post::ofCategory('Laravel')->SubscriptionType('Premium ')->get(); 

Summary

In above article we have seen Eloquent Query Scoping in Laravel. These are very useful when we have complex queries and dynamic filtering while fetching data from the database. Query scopes helps on code cleaning, re-usability and readability. For more you are always free to read detail Laravel's Documents on Query Scopes.

Hope you get something useful from the article. If you have any questions suggestions let me know. Thanks for reading.

Comments :