cerbero/query-filters

Filter Laravel Eloquent models based on query parameters.

Fund package maintenance!
cerbero90

1.8.0 2021-05-18 06:53 UTC

This package is auto-updated.

Last update: 2024-11-09 14:40:09 UTC


README

Author PHP Version Laravel Version Octane Compatibility Build Status Coverage Status Quality Score Latest Version Software License PSR-12 Total Downloads

SensioLabsInsight

Query Filters was fully inspired by this lesson on Laracasts, it provides a simple way to filter Eloquent models based on query parameters of an HTTP request.

Install

Via Composer:

composer require cerbero/query-filters

If your version of Laravel is prior to 5.5, you can register this package service provider by adding the following line to the list of providers in config/app.php:

'providers' => [
    ...
    Cerbero\QueryFilters\Providers\QueryFiltersServiceProvider::class,
]

This package includes a generator for query filter classes that by default are generated in app/QueryFilters. If you prefer a different path, you can set it in the config/query_filters.php file after running:

php artisan vendor:publish --tag=query_filters_config

Usage

Imagine to have a route for indexing all actors stored in the database. This route accepts query parameters to filter records, for example:

/actors?won_oscar&acting=0&acted-in=2000

In this case the route will need to display only actors who won at least one Oscar, are no longer acting but acted in 2000.

This can be achieved by generating a query filters class and optionally defining the allowed query parameters and related variable names via the following Artisan command:

php artisan make:query-filters ActorFilters 'won_oscar&acting=bool&acted-in=year'

That command will generate and populate with filters the ActorFilters class:

use Cerbero\QueryFilters\QueryFilters;

/**
 * Filter records based on query parameters.
 *
 */
class ActorFilters extends QueryFilters
{
    /**
     * Filter records based on the query parameter "won_oscar"
     * 
     * @return void
     */
    public function wonOscar()
    {
        // $this->query
    }

    /**
     * Filter records based on the query parameter "acting"
     * 
     * @param mixed $bool
     * @return void
     */
    public function acting($bool)
    {
        // $this->query
    }

    /**
     * Filter records based on the query parameter "acted-in"
     * 
     * @param mixed $year
     * @return void
     */
    public function actedIn($year)
    {
        // $this->query
    }
}

Please note how filter names are the equivalent camel case form of their related query parameters.

Filters are only applied if their query parameter is present in the HTTP request with a non-empty value, unless they need no value to function (e.g. won_oscar).

The $query property lets filters determine how queries change when they are applied:

public function wonOscar()
{
    $this->query->where('oscars', '>', 0);
}

public function acting($bool)
{
    $this->query->where('acting', $bool);
}

public function actedIn($year)
{
    $this->query->whereHas('movies', function ($movies) use ($year) {
        $movies->whereYear('release_date', '=', $year);
    });
}

After filters are defined, Eloquent models can apply them by using the FiltersRecords trait:

use Cerbero\QueryFilters\FiltersRecords;
use Illuminate\Database\Eloquent\Model;

class Actor extends Model
{
    use FiltersRecords;
}

Finally in your route you can filter actors by calling the method filterBy() of your model and passing the query filters:

use App\Actor;
use App\QueryFilters\ActorFilters;

...

public function index(ActorFilters $filters)
{
    return Actor::filterBy($filters)->get();
}

Alternatively you can hydrate query filters from a plain array:

use App\Actor;
use App\QueryFilters\ActorFilters;
use Illuminate\Http\Request;

...

public function index(Request $request)
{
    $filters = ActorFilters::hydrate($request->query());

    return Actor::filterBy($filters)->get();
}

Sometimes you may want to silently skip filters if their value is not valid. Instead of setting validation rules in HTTP requests, you may define them in the query filters class.

This avoids a failed validation to stop the filtering process and applies only valid filters while ignoring filters with values that do not pass the validation:

class ActorFilters extends QueryFilters
{
    protected function getRules()
    {
        return [
            'acting' => 'bool',
            'acted-in' => 'int|digits:4',
        ];
    }
}

Change log

Please see CHANGELOG for more information what has changed recently.

Testing

composer test

Contributing

Please see CONTRIBUTING and CONDUCT for details.

Security

If you discover any security related issues, please email andrea.marco.sartori@gmail.com instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.