Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel 5 Global Scope with Dynamic Parameter

We're having issues using a global scope with a dynamic query parameter. The global scope is based on the manager ID, but $model is empty, and $this refers to the manager scope not the model so $this->id is an undefined property. Is there a way to do something like this:

public function apply(Builder $builder, Model $model)
{
    return $builder->where('manager', $model->id); // $this->id
}

I'm assuming that $model is supposed to be the manager model, but since it is empty and I can't find any documentation on it I'm not entirely sure (if anyone can tell me in a comment I'd appreciate it). This is our global scope method in the Manager model:

protected static function boot()
{
    parent::boot();

    static::addGlobalScope(new ManagerScope);
}

Since global scopes don't require an explicit method to be applied I thought maybe adding something to the boot might allow for an extra parameter something like:

protected static function boot()
{
    parent::boot();

    static::addGlobalScope(new ManagerScope($this->id);
}

But this isn't allowed in a static method, which makes sense after I saw the error.

like image 946
mtpultz Avatar asked Dec 25 '22 02:12

mtpultz


2 Answers

Naturally global scopes are applied automatically and there are no way to pass parameters to them directly.

Therefore you can either stick with a dynamic local scope, which IMO makes more sense,

public function scopeForManager($query, $manager)
{
    return $query->where('manager', $manager->id);
}

Document::forManager($manager)->all();

or if the a manager info is available in some kind of global state (i.e. session) you can create some sort of ManagerResolver class

class ManagerScope
{
    protected $resolver;

    public function __construct(ManagerResolver $resolver)
    {
        $this->resolver = $resolver
    }

    public function apply(Builder $builder, Model $model)
    {
        return $builder->where('manager', $this->resolver->getManagerId());
    }    
}

and pass an instance of it into your scope

protected static function boot()
{
    parent::boot();
    static::addGlobalScope(new ManagerScope(new ManagerResolver());
}
like image 164
peterm Avatar answered Dec 26 '22 17:12

peterm


Update for Laravel 8.x

You can create a static function inside your model and then use it in a created scope. For example, let's say that you want to create a global search for all tables without creating a search function for each model.

In your scope class do the following:

<?php

namespace App\Scopes;
 
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
 
class SearchScope 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)
    {
        if(!empty($keyword = \request()->query('q')))
            $builder->where($model::searchField(), 'LIKE', '%' . $keyword . '%');
    }
}

Then create searchField() function in every model like this:

    <?php
    namespace Modules\Api\Models;
    
    use Illuminate\Database\Eloquent\Model;
    use App\Scopes\SearchScope;
    
    class Product extends Model
    {
        protected static function searchField(){
            return "products.name"; // Name of field to be used in WHERE clause while searching using LIKE operator
        } 
    
        protected static function booted()
        {
            static::addGlobalScope(new SearchScope);
        }
}

Bravo! Now you can use a dynamic variable in Laravel scope!!

like image 33
Kwizera Valens Avatar answered Dec 26 '22 16:12

Kwizera Valens