Criteria (Query Filter)

A way to encapsulate custom queries for your Eloquent models. These can be used as an alternative or complementary to Laravel's query scopesopen in new window.

How to create a new filter

Extend the Filter abstraction and implement the apply() method.

use Aedart\Database\Query\Filter;
use Illuminate\Contracts\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Contracts\Database\Query\Builder;

class VideoGamesCategoryFilter extends Filter
{
    public function apply(Builder|EloquentBuilder $query)
    {
        return $query->where('name', '=', 'Video Games');
    }
}

How to use

Once you have your filter created, you can apply it in your Eloquent model. Use the Filtering trait and call the applyFilters() method.

In your model

use Illuminate\Database\Eloquent\Model;
use Aedart\Database\Models\Concerns\Filtering;

class Category extends Model
{
    use Filtering;
}

Apply filter

$result = Category::applyFilters(new VideoGamesCategoryFilter())->first();

Apply Multiple Filters

The applyFilters() accepts either a single filter instance or a list of filters.

$result = Category::applyFilters([
    new OldRecords(),
    new HasDiscounts(),
    new NotDeleted()
])->get()

Applicability

Each filter has a isApplicable() method. It is used to determine whether the filter must be applied or not. By default, this method will always return true. However, if you need to exclude a filter, then you should overwrite this method.

Two parameters are provided:

  • $query: The current query scope.
  • $filters: List of all the filters that are about to be applied.

These parameters can be used for your determination logic, if it makes sense for you. Otherwise, simply ignore them.

use Aedart\Database\Query\Filter;
use Acme\Filters\IsDeleted;
use Illuminate\Contracts\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Contracts\Database\Query\Builder;

class OldRecords extends Filter
{
    public function isApplicable(Builder|EloquentBuilder|null $query = null, $filters = []): bool
    {
        // Prevent this filter, if another specific filter is about
        // to be applied...
        foreach($filters as $filter) {
            if ($filter instanceof IsDeleted) {
                return false;
            }
        }
        
        return true;
    }

    // ... remaining not shown ... //
}

Whenever the above shown filter is used, in combination with a IsDeleted filter, it will be ignored and not applied.

$result = Category::applyFilters([
    new OldRecords(), // Will be ignored
    new IsDeleted()
])->get()

Field Criteria (Field Filter)

If you require filters that add where <expression> constraints for a single field (column) on your query, then you can choose to inherit from the FieldFilter. This abstraction allows you to create slightly more constraints to be applied using either AND or OR logical operator.

use Aedart\Database\Query\FieldFilter;
use Aedart\Contracts\Database\Query\FieldCriteria;
use Illuminate\Contracts\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Contracts\Database\Query\Builder;

class StringFilter extends FieldFilter
{
    public function apply(Builder|EloquentBuilder $query)
    {
        if ($this->logical() === FieldCriteria::OR) {
            return $query->orWhere($this->field(), $this->operator(), $this->value());
        }

        return $query->where($this->field(), $this->operator(), $this->value());
    }
}

Usage

use Aedart\Contracts\Database\Query\FieldCriteria;

$result = Category::applyFilters([
    StringFilter::make('name', 'LIKE', '%games%', FieldCriteria::OR),
    StringFilter::make('name', 'LIKE', '%video%', FieldCriteria::OR),
])->get()

How you choose to design your "field" specific filters, is left to your imagination. For additional information, please review the source code.