Introduction:-
Laravel has a powerful feature named query scopes. It is one of the many arrows in its quiver. Query scope is used to retrieve and filter the data in a comfortable way reducing the headache of the user. Eloquent ORM included with Laravel provides a number of ways to work with the database and query scopes come under this category. Generally, every table has a model in Laravel except pivot table which is optional and we use query scopes in that model or can define query scope in other file and can include it in the model.
Why we need Query Scopes:-
Suppose the user has to fetch the posts and display it on various pages whether based on tags, categories, user search records. The common requirement of all these posts should be that all posts should be active i.e. there status should be set to 1 in the database in order to get displayed on the website. So, the user is left with two choices either to apply status check query on all posts i.e.
$posts = Post::where('status', '=' , 1)->get();
or
to use a query scope where this status check condition is not needed to be implemented.
Query Scope Types:-
There are two types of query scopes in Laravel:-
User can decide which Query Scope to use depending on the requirements.
Global Scopes:- Global Scopes is used when you want to apply certain constraints to all queries automatically. There are two ways to use global scopes.
First method – User can create a class which implements Illuminate\Database\Eloquent\Scope interface. In this class, you are required to use one method named apply in which where constraints for the query are defined.
1. Create a file named ActiveScope in App Folder.
<:?php namespace App\Scopes; use Illuminate\Database\Eloquent\Scope; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; 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', 1); } }
2. Include this File in Required Model and override the boot method.
<?php namespace App; use App\Scopes\ActiveScope; use Illuminate\Database\Eloquent\Model; class Post extends Model { /** * The "booting" method of the model. * * @return void */ protected static function boot() { parent::boot(); static::addGlobalScope(new ActiveScope); } }
After that User has to add the query scope in the required model. User can use this scope in one than one model as per the requirement. After this, if the user executes the post query in laravel it would automatically fetch all the posts whose status is 1 only.
For e.g.
$posts = Post::all();
is similar to
$posts = Post::where('status', '=' , 1)->get();
Second method – If the user wants to define a global query scope without creating a separate class that can also be done.
1. Define global query scope using closures in the model.
<?php namespace App; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; class Post extends Model { /** * The "booting" method of the model. * * @return void */ protected static function boot() { parent::boot(); static::addGlobalScope('status', function (Builder $builder) { $builder->where('status', 1); }); } }
There may be some case arises when the user wants to get all posts irrespective of the status of the post in that case withoutGlobalScope method is used.
This method is used when Scope is defined in a separate class.
Post::withoutGlobalScope(ActiveScope::class)->get();
This method is used when Global Scope is defined using closure.
Post::withoutGlobalScope('active')->get();
Local Scopes:- In Local Query Scopes you can define constraints in the model and use in your website but it would not be automatically added to every query like global scope. You have to include it manually wherever this local scope is needed. It is useful to reduce the complexity of our query.
For e.g.:-
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { /** * Scope a query to only show active posts. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopeActive($query) { return $query->where(status,1); }
Here to define a query scope a method named scopeActive is created. The method naming is important in this case as scope prefix is to be added in front and method name should be in camelCase and now to use this local scope:-
$posts = Post::active()->get();
Local Scope also includes dynamic scope which means to pass parameters to the scope method.
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { /** * Scope a query to only include posts of a given status. * * @param \Illuminate\Database\Eloquent\Builder $query * @param mixed $type * @return \Illuminate\Database\Eloquent\Builder */ public function scopeActive($query, $type) { return $query->where('status', $type); }
Suppose there are three types of status in post table i.e. unapproved(where id =0), Approved(where id=1) and Rejected(id=2). So Instead of making three different methods you can define one method and can pass parameters as per the requirement. To get all rejected posts by admin use the following code:
$posts = Post::active(2)->get();