Laravel Scope — An Introduction
Scoping is one of the superpowers that eloquent grants to developers when querying a model. Scopes allow developers to add constraints to queries for a given model.
Utilizing Eloquent’s Scoping helps us to follow the DRY principles when writing queries for our models. When a particular query is repeated over and over again, at many places in our business logic, then it should be abstracted into a scope.
Scopes can be defined either in the model class or in a separate scope class, also a scope can be local, or global, a scope also can be dynamic, i.e receiving parameters when it is been used.
Local Scope
/**
* Scope a query to only include popular users.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopePopular($query)
{
return $query->where('votes', '>', 100);
}
The above code block shows an example of a local scope, a scope is just a method in your model class, that starts with the word “scope” and then the scope name, and can be used with your model.
scopePopular — Model::popular()->get();
scopeTeacher — Model::teacher()->get();
scopeMortages — Model::mortages()->get();
scopeSubscribers — Model::subscribers()->get();
As seen in the code block, the scope method must be declared with a parameter, conventionally named $query but can be changed to any other properly formed variable name. This $query parameter is the Eloquent builder instance which is used in mostly all our eloquent model queries.
Following, the above explanation, I want to believe you have figured out other scope-like methods that are available to use when querying models in Laravel applications:
where, orWhere, with, all, paginate, distinct, onlyTrashed, withTrashed, withoutTrashed, create etc
Global Scope
/**
* The "booted" method of the model.
*
* @return void
*/
protected static function booted()
{
static::addGlobalScope('age', function (Builder $builder) {
$builder->where('age', '>', 200);
});
}
As seen above, global scopes are added a bit differently from the local scope, they are added in the static booted method of the model using a static method addGlobalScope, this receives two arguments, the name of the scope and a closure. The scope’s closure, like in the local scope, declares a $builder parameter (local scope uses $query), and these variables can be renamed to suit the developer’s choice.
To make queries without the declared global scope, the withoutGlobalScope static method is called with the model and provided the scope name:
Model::withoutGlobalScope($globalScopeName)->get();//replace the variable $globalScopeName with the name of the scope
Difference between local and global scope
Local scopes are user applied, used when the query is built while global scopes are applied to all queries on that model.
An example of global scope used by Laravel is the retrieving of non-deleted records (if using soft deletes) when querying a model, and to retrieve records with the deleted records, you make use of the local scope withTrashed
Dynamic Local Scope
This kinds of scopes are local scopes that receive an argument from the developer when using the scope, defaults scope-like methods like where, orWhere counts as dynamic and to create a dynamic scope, create a local scope that has a second, third or more parameters as fit your need:
/**
* Scope a query to only include users of a given type.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param mixed $type
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeOfType($query, $type)
{
return $query->where('type', $type);
}
The $type parameter has to be provided by the developer when using the ofType scope, else an error will be thrown.
Usecases
- User Roles: If you’re using a role column in a User model to determine if a user is a teacher or a student:
//local scope
public function scopeTeachers($query)
{
return $query->where('role', 'teacher');
} public function scopeStudents($query)
{
return $query->where('role', 'student');
}or
//dynamic local scope
public function scopeType($query,$userType)
{
return $query->where('role', $userType);
}Model::teachers()->get(); or Model::type('teacher')->get();
Model::students()->get(); or Model::type('student')->get();
- Publications: Publication posts in your application have the status column to determine if a post is published or not, this would be a perfect scenario to use the global scope to filter posts that are published.
protected static function booted()
{
static::addGlobalScope('status', function (Builder $builder){
$builder->where('status',1);
});
}
With this, calling Model::all() will retrieve only posts that have the value 1 in the status column.
There are numerous possible scenarios where scopes, both local and global come in handy, and making use of it will help keep your controllers clean.
Follow me on twitter: https://twitter.com/drumzminister