Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default scope for Eloquent-models?

Here is a sample database-table (users):

id - int(11) auto_increment
name - varchar(100)
banned - int(1)

The column banned is a boolean value which is 0 (false) by default. If an user has been banned, the value is 1.

I'd like to exclude any banned users from all queries by default. I could create a query scope and then use that everywhere. However, I'd much more prefer simply having that check on by default.

I could also create a newQuery-method of my own, like so:

// Inside User-model, which extends Eloquent
public function newQuery($excludeDeleted = true)
{
    $builder = parent::newQuery($exludeDeleted);
    $builder->where('banned', '=', '0');
    return $builder;
}

However, this way I wouldn't be able to switch off this behaviour. I might want to see the banned users in my private admin panel, but would not be able to, as this restriction would be applied to any query done via Eloquent.

Any idea on how to solve this problem?

like image 443
Martti Laine Avatar asked Feb 16 '14 14:02

Martti Laine


2 Answers

Replying here for others that may be looking for a similar answer.

You can create a global scope, have the model use the global scope by default, then use the withoutGlobalScope when you want to make a query without that scope applied.

See Laravel docs: https://laravel.com/docs/5.8/eloquent#global-scopes


So in your case you would create a new global scope to query users that are not banned.

<?php

namespace App\Scopes;

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

class ExcludeBannedScope 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('banned', '=', 0);
    }
}

Apply the global scope to your User model.

<?php

namespace App;

use App\Scopes\ExcludeBannedScope;
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 ExcludeBannedScope);
    }
}

This will now added the where banned = 0 to all queries.

In your admin section where you want to see all users including banned you can do

User::withoutGlobalScope(\App\Scopes\ExcludeBannedScope::class)->get();
like image 183
David Maksimov Avatar answered Nov 16 '22 02:11

David Maksimov


Laravel provides Global Scopes for exactly this purpose. From the docs:

class AgeScope implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        return $builder->where('age', '>', 200);
    }
}

class User extends Model
{
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope(new AgeScope);
    }
}
like image 22
Ben Claar Avatar answered Nov 16 '22 01:11

Ben Claar