Custom Eloquent Query Builder Class (local scopes Class)

Custom Eloquent Query Builder Class (local scopes Class)

Lets refactor our Laravel Eloquent models using a underrated a class based query scope.

Lets refactor our Laravel Eloquent models using a underrated a class based query scope.

Laravel eloquent provides a simple elegant way of making database queries in your application. It uses Models as an object to interact with a table.

Task::get();
Task::create($someData);

Eloquent, has even helpful methods, to fetch data based on conditions. simple methods like.

Eloquent helpers

Task::where('status', 'archived')->get();

Local scopes

Further more, you can refactor such query using scopes,

Class Task extends Model 
{
       public function scopeArchived($query)
        {
                return $query->where('status', 'archived');
        }
}

Our previous query becomes

Task::archived()->get();

If you have a global scope used on multiple Models, you can create a class that implements the Scope Class.

Global scopes

class ArchivedScope implements Scope
{
        public function apply(Builder $builder, Model $model)
    {
        $builder->where('status', 'archived');
    }
}

Now on every class where we want to apply the scope, we boot the model with the scope.

The boot or booting method are event hooks we can use for scopes or other things, like listeners, or observers.

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

    static::addGlobalScope(new ArchivedScope);
}

Renaming the global scope per model

This will register the scope on the boot event hook. so we would always scope to ArchivedScope. If for wahtever reason, you want to change the name of the scope.

static::addGlobalScope('newScopeName', new ArchivedScope);

Local scopes class.

The underrated option we have, is a class based local scope, to make scopes more maintanable embracing the Laravel Eloquent capabilities.

namespace App\Models\Builders;

use Illuminate\Database\Eloquent\Builder;

class TaskQueryBuilder extends Builder
{
    public function archived(): self
    {
        return $this->whereStatus('archived');
    }
}

Then we set this as the query builder for our model

Class Task extends Model
{
        public function newEloquentBuilder($query): TaskQueryBuilder
        {
            return new TaskQueryBuilder($query);
        }
}

If you are into static analysis. code perfection, good news, we got you covered. You can only have to specify the query() method on the Task Model class.

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Models\Builders\TaskQueryBuilder;

Class Task extends Model
{
    public static function query(): TaskQueryBuilder
    {
        return parent::query();
    }

    // final model looks like 

    public function newEloquentBuilder($query): TaskQueryBuilder
    {
        return new TaskQueryBuilder($query);
    }

}

Eloquent Query Builder class

Our final custom Eloquent Query Builder class

namespace App\Models\Builders;

use Illuminate\Database\Eloquent\Builder;

class TaskQueryBuilder extends Builder
{
    public function archived(): self
    {
        return $this->whereStatus('archived');
    }
}