henzeb/query-filter-builder

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# Query Filter Builder
[![Build Status](https://github.com/henzeb/query-filter-builder/workflows/tests/badge.svg)](https://github.com/henzeb/query-filter-builder/actions)
[![Latest Version on Packagist](https://img.shields.io/packagist/v/henzeb/query-filter-builder.svg)](https://packagist.org/packages/henzeb/query-filter-builder)
[![Total Downloads](https://img.shields.io/packagist/dt/henzeb/query-filter-builder.svg)](https://packagist.org/packages/henzeb/query-filter-builder)
[![Test Coverage](https://api.codeclimate.com/v1/badges/03335803f33d12bc45bd/test_coverage)](https://codeclimate.com/github/henzeb/query-filter-builder/test_coverage)

Whenever you need filters on your API's endpoints, this package gives you 
a nice and simple interface that allows you to add filters without the 
need of a thousand parameters passed to your methods or writing SQL queries 
inside your controllers.

This comes with support for Laravel. If you'd  like to contribute
for other frameworks, see [Contributing](CONTRIBUTING.md).

## Installation
You can install the package via composer:

```bash
composer require henzeb/query-filter-builder
```

## Usage
See [here](doc/LARAVEL.md) for Laravel specific usage.

In your controller you may build up something like this, based on parameters
given by the user of your application.

```php
use Henzeb\Query\Filters\Query;

$filter = (new Query())
    ->nest(
        (new Query)
            ->nest(
                (new Query)
                    ->is('animal', 'cat')
                    ->less('age', 10)
            )->or()
            ->nest(
                (new Query)
                    ->is('animal', 'dog')
                    ->between('age', 5, 7)
            )
    )->in('disease', 'diabetes', 'diarrhea')
    ->limit(50)
    ->offset(50);
```
Building the query using Laravel's query builder, can be done as such:
```php
use DB;
use Henzeb\Query\Illuminate\Builders\Builder;

$query = DB::table('patients')
    ->where('vet_id', 1);
    
$filter->build(new Builder($query));
```
This would result in the following query:
```sql
select *
from `patients`
where `vet_id` = ?
  and (
          (`animal` = ? and `age` < ?)
          or 
          (`animal` = ? and `age` between ? and ?)
      )
  and `disease` in (?, ?)
limit 50 offset 50
```

Note: a query filter can never start with `or`. This prevents data-leak
situations where one could get for example all records of it's own or the 
dogs from another veterinarian:
```sql
select * from `patients` where `vet_id` = ? or `animal` = ?
```
Where one could get all records of it's own or the dogs from another 
veterinarian.

### Custom filters
You can also create your own filters in case you need something specific.

When building with Laravel, your custom filter could look like this:
```php
use Henzeb\Query\Illuminate\Filters\Contracts\Filter;

use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\Builder as IlluminateBuilder;

class OwnerCountFilter implements Filter
{
    public function __construct(private int $count)
    {
    }

    public function build(EloquentBuilder|IlluminateBuilder $builder): void
    {
        $builder->whereRaw(
            '(
                select count(1) 
                from `owners_patients` 
                where `owners_patients`.`patient_id` = `patients`.`id`
            ) = ?',
            [$this->count]
        );
    }
}
```
You can then call it like this:
```php
use Henzeb\Query\Filters\Query;
use App\Filters\YourCustomFilter;

$filter = (new Query)->filter(OwnerCountFilter(1));
```
Which would result in a query like this:
```sql
select *
from `patients`
where `vet_id` = ?
  and (
            (
                select count(1) 
                from `owners_patients` 
                where `owners_patients`.`patient_id` = `patients`.`id`
            ) = ?
      )
```

## Creating your own builder.
Simply implement the `Henzeb\Query\Builders\Contracts\QueryBuilder` interface.

### Custom filters
The custom filters approach might feel a bit strange. You must create your 
own custom filter interface, as the default 
`Henzeb\Query\Illuminate\Filters\Contracts\Filter` interface does not have any
methods. 

See `Henzeb\Query\Illuminate\Builders\Builder` for an example on proxying in 
order to enable your IDE's typehinting.

If you have a better approach, please let me know or submit a pull-request.

### Testing

```bash
composer test
```

### Changelog

Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.

## Contributing

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

### Security

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

## Credits

- [Henze Berkheij](https://github.com/henzeb)

## License

The GNU AGPLv. Please see [License File](LICENSE.md) for more information.