Applying conditions to queries gives you the power to retrieve and present filtered data in every imaginable manner. Some of these conditions will be used more than others, and Laravel provides a solution for cleanly packaging these conditions into easily readable and reusable statements, known as query scope. In this tutorial I'll show you how to easily integrate query scopes into your Laravel models.
Consider a filter that only retrieves users who are active. You could use the following where condition to retrieve those users:
$users = User::where('active', '=' , 1)->get();
however this where condition might repeat in multiple locations throughout your application. If so, you can DRY up the code little bit by instead using a Query scope. A scope is just a convenience method you can add to your model which encapsulates the syntax used to execute a query such as the above. Scopes are defined by prefixing the name of a method with scope, as demonstrated here:
class User extends Model { /** * Scope a query to only include active users. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopeActive($query) { return $query->where('active', 1); } }
With the scope defined, you may call the scope methods when querying the model like so:
$users = User::active()->orderBy('created_at')->get();
Laravel Query Scopes can be categorized in to three type.
- Global Scopes
- Local Scopes
- Dynamic Scopes
Global Scopes
Global scopes allow to add constraints to all queries for a given model. Laravel's own soft delete functionality utilizes global scopes to only pull "non-deleted" Eloquent models from the database. Writing your own global scopes can provide a convenient, easy way to make sure every query for a given model receives certain constraints.
Creating Global Scopes
Creating a global scope is simple. Define a class that implements the Illuminate\Database\Eloquent\Scope
interface. This interface requires you to implement one method: apply
. The apply
method may add where
constraints to the query as needed:
<?php namespace App\Scopes; use Illuminate\Database\Eloquent\Scope; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; class AgeScope 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('age', '>', 200); } }
If your global scope is adding columns to the select clause of the query, you should use theaddSelect
method instead ofselect
. This will prevent the unintentional replacement of the query's existing select clause.
Applying Global Scopes
To assign a global scope to a model, you should override a given model's boot
method and use the addGlobalScope
method:
<?php namespace App; use App\Scopes\AgeScope; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * The "booting" method of the model. * * @return void */ protected static function boot() { parent::boot(); static::addGlobalScope(new AgeScope); } }
After adding the scope, a query to User::all()
will produce the following SQL:
select * from `users` where `age` > 200
Anonymous Global Scopes
Eloquent also allows you to define global scopes using Closures, which is particularly useful for simple scopes that do not warrant a separate class:
<?php namespace App; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; class User extends Model { /** * The "booting" method of the model. * * @return void */ protected static function boot() { parent::boot(); static::addGlobalScope('age', function (Builder $builder) { $builder->where('age', '>', 200); }); } }
Removing Global Scopes
If you would like to remove a global scope for a given query, you may use the withoutGlobalScope
method. The method accepts the class name of the global scope as its only argument:
User::withoutGlobalScope(AgeScope::class)->get();
Or, if you defined the global scope using a Closure:
User::withoutGlobalScope('age')->get();
If you would like to remove several or even all of the global scopes, you may use the withoutGlobalScopes
method:
// Remove all of the global scopes... User::withoutGlobalScopes()->get(); // Remove some of the global scopes... User::withoutGlobalScopes([ FirstScope::class, SecondScope::class ])->get();
Local Scopes
Local scopes allow you to define common sets of constraints that you may easily re-use throughout your application. For example, you may need to frequently retrieve all users that are considered "popular". To define a scope, prefix an Eloquent model method with scope
.
Scopes should always return a query builder instance:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * 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); } /** * Scope a query to only include active users. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopeActive($query) { return $query->where('active', 1); } }
Utilizing A Local Scope
Once the scope has been defined, you may call the scope methods when querying the model. However, you should not include the scope
prefix when calling the method. You can even chain calls to various scopes, for example:
$users = App\User::popular()->active()->orderBy('created_at')->get();
Dynamic Scopes
Sometimes you may wish to define a scope that accepts parameters. To get started, just add your additional parameters to your scope. Scope parameters should be defined after the $query
parameter:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * 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); } }
Now, you may pass the parameters when calling the scope:
$users = App\User::ofType('admin')->get();
Using Scopes with Model Relations
You'll often want to use scopes in conjunction with model relations. For instance, you can retrieve a list of popular posts associated with a specific user:
$user = User::find(34); $posts = $user->posts()->popular()->get();
If you have any other questions, experience or insights on "Using Query Scopes with Laravel 5" please feel free to leave your thoughts in the comments bellow which might be helpful to someone some day!
Be the first one to write a response :(
{{ reply.member.name }} - {{ reply.created_at_human_readable }}