Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/aldemeery/sieve
A simple, clean and elegant way to filter Eloquent models.
https://github.com/aldemeery/sieve
eloquent filter filtration laravel
Last synced: about 1 month ago
JSON representation
A simple, clean and elegant way to filter Eloquent models.
- Host: GitHub
- URL: https://github.com/aldemeery/sieve
- Owner: aldemeery
- License: mit
- Created: 2019-03-05T19:22:22.000Z (almost 6 years ago)
- Default Branch: master
- Last Pushed: 2022-02-19T15:15:06.000Z (almost 3 years ago)
- Last Synced: 2024-09-18T11:30:20.034Z (3 months ago)
- Topics: eloquent, filter, filtration, laravel
- Language: PHP
- Size: 67.4 KB
- Stars: 121
- Watchers: 5
- Forks: 3
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Sieve - Clean & Easy Eloquent Filtration
* [Installation](#installation)
* [Usage](#usage)
* [Creating Filters](#creating-filters)
* [Filtering](#filtering)
* [Mapping Values](#mapping-values)A minimalist, ultra-lightweight package for clean, intuitive query filtering.
With Sieve, your filtration logic is simplified from something like this:
```php
public function index(Request $request)
{
$query = Product::query();if ($request->has('color')) {
$query->where('color', $request->get('color'));
}if ($request->has('condition')) {
$query->where('condition', $request->get('condition'));
}if ($request->has('price')) {
$direction = $request->get('price') === 'highest' ? 'desc' : 'asc';
$query->orderBy('price', $direction);
}return $query->get();
}
```to this:
```php
public function index(Request $request)
{
return Product::filter($request->query())->get();
}
```---
## Installation
> [!IMPORTANT]
> This package requires Laravel 11.0 or higher and PHP 8.2 or higher.You can install the package via composer:
```bash
composer require aldemeery/sieve
```---
## Usage
Enabling filtration for a model is as easy as adding the `Aldemeery\Sieve\Concerns\Filterable` trait to it:
```php
use Aldemeery\Sieve\Concerns\Filterable;
use Illuminate\Database\Eloquent\Model;class Product extends Model
{
use Filterable;// ...
}
```
The `Filterable` trait introduces a `filter` [local scope](https://laravel.com/docs/eloquent#local-scopes) to your model, which accepts an associative array for filtration:```php
public function index(Request $request)
{
return Product::filter($request->query())->get();
}
```Now you're ready to create your filter classes.
---
### Creating filters
To create a filter, create a class that implements the `Aldemeery\Sieve\Contracts\Filter` interface.
You can either create a filter class using the `make:filter` artisan command, which will place the filter in the `app/Http/Filters` directory.
Alternatively, you can create a filter class manually and place it wherever you prefer:```bash
php artisan make:filter Product/ColorFilter
```This generates a `ColorFilter` class in the `app/Filters/Product` directory:
```php
*/
class ColorFilter implements Filter
{
public function map(mixed $value): mixed
{
return match ($value) {
default => $value,
};
}public function apply(Builder $query, mixed $value): void
{
// $query->where('id', $value);
}
}
```Here, `apply` defines the filtration logic, while `map` can transform input values if needed before passing them to `apply`
> [!IMPORTANT]
> Before a value is passed to the `apply` method, it's first passed to the `map` method.
>
> If you do not need to map values into other values, you should just leave the `map` method as it is.Check out this examples:
```php
public function map(mixed $value): mixed
{
return match ($value) {
'yes' => true,
'no' => false,
'1' => true,
'0' => true,
default => $value,
};
}public function apply(Builder $query, mixed $value): void
{
// Assuming filter was called like this: Product::filter(['in_stock' => 'yes'])->get();
// Or like this: Product::filter(['in_stock' => '1'])->get();
// In both cases, $value would be `true`$query->where('in_stock', $value);
}
```With an instance of `Illuminate\Database\Eloquent\Builder` passed to `apply`, you gain access to its full capabilities, allowing you to perform a wide range of operations:
#### Example 1 - Ordering:
```php
public function apply(Builder $query, mixed $value): void
{
$query->orderBy('price', $value);
}
```#### Example 2 - Relations:
```php
public function apply(Builder $query, mixed $value): void
{
$query->whereHas('category', function($query) use ($value): void {
$query->where('name', $value);
});
}
```---
### Filtering
Once you have created your filters and defined your filtration logic, It's time now to actually use the filter, which can be done in two ways:
- [Passing a filters array](#passing-a-filters-array) as a second parameter to the `filter` scope.
- [Defining model filters](#defining-model-filters) inside the model itself.#### Passing a filters array:
Use this when you want to apply a filter to a single query:
```php
public function index(Request $request)
{
return Product::filter($request->query(), [
// "color" here is the key to be used in the query string
// e.g. https://example.com/products?color=red
"color" => \App\Filters\Product\ColorFilter::class,
])->get();
}
```In the above example, the `ColorFilter` is applied *only* for this query.
#### Defining model filters:
Alternatively, if you want a filter to be associated with a model and applied every time the filter method is called, you can add a `filters` method to your model that returns an array mapping keys to their corresponding filter classes:```php
*/
private function filters(): array
{
return [
'color' => \App\Filters\Product\ColorFilter::class,
];
}
}
```Now everytime you call the `filter` method on the model, you will have the `ColorFilter` applied to your query:
```php
public function index(Request $request)
{
// The `ColorFilter` filter is applied.
return Product::filter($request->query())->get();
}
```> [!IMPORTANT]
> Only filters with keys present in the data array will be applied. Any filters not included in the array will be ignored.
>
> For instance, if your filter array includes only the `color` key, only the corresponding `ColorFilter` will be executed, while any other filters will have no effect on the query.---
### Mapping Values
In some cases, you may want to use more user-friendly values that do not directly correspond to the values needed for filtration.
This is where the map method comes in handy.Before any value reaches the `apply` method, it is first processed by the map method.
This allows you to transform incoming values into something more meaningful for your application.#### Example:
Imagine you want to sort products by price but using the query string, but you prefer using labels like `..?price=lowest` or `..?price=highest` instead of technical terms like `..?price=asc` or `..?price=desc`.You can achieve this by using the `map` method, as shown below:
```php
*/
class PriceFilter implements Filter
{
public function map(mixed $value): mixed
{
return match ($value) {
'lowest' => 'asc',
'highest' => 'desc',
default => $value,
};
}public function apply(Builder $query, mixed $value): void
{
// After mapping, $value will be 'asc' for 'lowest' and 'desc' for 'highest'.
$query->orderBy('price', $value);
}
}
```With this implementation, you can present a more intuitive interface to users while maintaining the necessary functionality for sorting in your queries.