PHPackages                             matejsvajger/laravel-distillery - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Search &amp; Filtering](/categories/search)
4. /
5. matejsvajger/laravel-distillery

ActiveLibrary[Search &amp; Filtering](/categories/search)

matejsvajger/laravel-distillery
===============================

An elegan way of filtering Eloquent models.

v0.4.0(7y ago)34MITPHPPHP ^7.1.3

Since Nov 9Pushed 7y ago1 watchersCompare

[ Source](https://github.com/matejsvajger/laravel-distillery)[ Packagist](https://packagist.org/packages/matejsvajger/laravel-distillery)[ RSS](/packages/matejsvajger-laravel-distillery/feed)WikiDiscussions master Synced yesterday

READMEChangelogDependencies (2)Versions (6)Used By (0)

[![](resources/images/distillery-logo.svg)](resources/images/distillery-logo.svg)

[![Total Downloads](https://camo.githubusercontent.com/4bee7bb855e68120170805a18c7bd811ae4efa052e927792fbcf4ef430b58769/68747470733a2f2f706f7365722e707567782e6f72672f6d6174656a7376616a6765722f6c61726176656c2d64697374696c6c6572792f642f746f74616c2e737667)](https://packagist.org/packages/matejsvajger/laravel-distillery)[![License](https://camo.githubusercontent.com/710d87405ab782f213bc761d9a690c36d797923b6f5facbe0898c5609c91f58b/68747470733a2f2f706f7365722e707567782e6f72672f6d6174656a7376616a6765722f6c61726176656c2d64697374696c6c6572792f6c6963656e73652e737667)](https://packagist.org/packages/matejsvajger/laravel-distillery)

Introduction
------------

[](#introduction)

Laravel Distillery provides an elegant way for filtering and paginating Eloquent Models. Distillery taps into native Laravel API Resource Collections and builds a paginated filtered result of models/resources while making it possible to use the Laravel's pagination templates.

Installation &amp; Configuration
--------------------------------

[](#installation--configuration)

You may use Composer to install Distillery into your Laravel project:

```
 composer require matejsvajger/laravel-distillery
```

After installing Distillery, publish its config using the `vendor:publish` Artisan command.

```
php artisan vendor:publish --tag=distillery-config
```

After publishing Distillery's config, its configuration file will be located at `config/distillery.php`. This configuration file allows you to configure your application setup options and each configuration option includes a description of its purpose, so be sure to thoroughly explore this file.

Quickstart
----------

[](#quickstart)

Let's say you have a long list of products on route: `/product-list` all you need to do is attach `Distillable` trait to your Product model:

```
namespace App\Models

use Illuminate\Database\Eloquent\Model;
use matejsvajger\Distillery\Traits\Distillable;

class Product extends Model {
    use Distillable;

    protected $distillery = [
        'hidden' => [
            //
        ],
        'default' => [
            //
        ]
    ];

	...
}
```

Distillable trait adds a `static function distill($filters = null)`. In your controller that handles the `/product-list` route just replace your Product model fetch call (ie:`Product:all()`) with `::distill()`:

```
class ProductListController extends Controller
{
	public function index()
	{
		return view('product.list',[
			'products' => Product::distill()
		]);
	}
}
```

### Pagination

[](#pagination)

`distill()` will return a paginated response of 15 items. This is the default Eloquent model value on `$perPage` property. You can adjust it by overwriting the value in your model or set a default value for `limit` in [distillery model property](#default-filter-values-per-model).

To add pagination links to the view call `$products->links();` in your blade template:

```
...

	@foreach($products as $product)

			{{ $product->id }}
			{{ $product->name }}
			{{ $product->description }}
			{{ $product->price }}

	@endforeach
...

		{{ $products->links() }}

...
```

There you have it, a paginated list of Product models.

*What!? This is just like Laravel's Paginator!* Right, we'll want to filter it too, eh? Ok, carry on.

### Filtering

[](#filtering)

If we want to filter the above product list with a search query on `name` and `description` we'll need a search filter for product model. Let's create it:

```
php artisan distillery:filter Search Product
```

This will scaffold a Search filter in `app/Filters/Product/Search.php`

Generated class implements `apply(Builder $builder, $value)` method, that receives Eloquent builder and the filter value.

For the above Search example we would do something like this:

```
namespace App\Filters\Product;

use Illuminate\Database\Eloquent\Builder;
use matejsvajger\Distillery\Contracts\Filter;

class Search implements Filter
{
    public static function apply(Builder $builder, $value)
    {
        return $builder->where(function ($query) use ($value) {
            $query
                ->where('name', 'like', "{$value}%")
                ->orWhere('description', 'like', "%{$value}%");
        });
    }
}
```

To apply the filter to the previous product list you can just add a search query string parameter to the url:

`/product-list?search=socks` and the collection will be automatically filtered and pagination links will reflect the set filters.

For more examples on filters check the [**Examples**](#examples) section.

How it works
------------

[](#how-it-works)

The idea behind Distillery is that every request parameter is a filter name/value pair. Distillery loops through all request parameters, checks if the filter for selected model exists and builds a filtered resource collection based on their values.

By default Distillery predicts that you have:

- models stored in `app/Models`,
- resources stored in `app/Http/Resources`,
- and that filters will be in `app/Filters`.

All values are configurable through the config file.

Filter names

- *page* and
- *limit*

are **reserved** for laravel paginator.

---

Digging deeper
--------------

[](#digging-deeper)

### Artisan Command `distillery:filter`

[](#artisan-command-distilleryfilter)

Distillery comes with an Artisan generator command that scaffolds your filter classes for existing models. Signature has two parameters:

`'distillery:filter {filter} {model?}'`

- `{filter}` Filter name (**required**)
- `{model?}` Model class name (**optional**)

If you pass in the model name the filter will be generated in the sub-namespace: `App\Filters\{Model}`. Without optional model paramater the filteres are generated in `App\Filters` for general usage on multiple models.

To enable **fallback** to general filters you need to set `'fallback' => true` on the [distillery model property](#enable-fallback-to-general-filters).

During generation you'll be offered to choose from some standard filter templates:

> #### Blank template
>
> [](#blank-template)
>
> The generated class returns `$builder` without modifications. You'll need write the logic yourself.

> #### Sorting template
>
> [](#sorting-template)
>
> You define a list of model fields you wish to sort on and select a default sorting field and direction.

> #### Search template
>
> [](#search-template)
>
> Define the model field to search on. A filter with `"like {$value}%"` will be generated.

### Serverside filter values

[](#serverside-filter-values)

Sometimes you'll want to attach additional filters on server-side. By default you don't need to pass any filters in. Distilliery will pull them out of request. Usually you'll have a seo route, that already should return a fraction of models instead of all; like a category route for products: `/category/summer-clothing?search=bikini`.

Normally you wouldn't want to pass the category id in paramaters since it's already defined with a seo slug.

You can add aditional filters not defined in URI or overwrite those by passing an array into distill function. *i.e.:* If you have a Category filter that accepts an id, attach it in your controller:

```
public function list(Request $request, Category $category)
{
    return view('product.list',[
        'products' => Product::distill([
            'category' => $category->id,
        ]);
    ]);
}
```

### Distillery Facade

[](#distillery-facade)

Distillery comes with a facade that gives you an option to distill any model without the Distillable trait. It takes two parameters, Model FQN and filter array.

```
Distillery::distill(Product::class, $filters);
```

### Model Resources

[](#model-resources)

If you're using Distillery as API endpoints you probabbly don't want to expose your whole model to the world or maybe you want to attach some additional data. Distillery checks if [Eloquent resources](https://laravel.com/docs/5.7/eloquent-resources) exist and maps the filtered collection to them, otherwise it returns normal models.

If you don't have them just create them with Artisan

```
php artisan make:resource Product
```

and check out the [docs](https://laravel.com/docs/5.7/eloquent-resources#writing-resources) on how to use them.

### Default filter values per model

[](#default-filter-values-per-model)

It is possible to define default filter values per model. For example if you want a default filter value for some model you can do it with a 'default' key in a `protected $distillery` array on the model itself:

```
class User extends Model {
    protected $distillery = [
        'default' => [
            'sort' => 'updated_at-desc'
        ]
    ];
}
```

### Hide filters from URI QueryString

[](#hide-filters-from-uri-querystring)

There is a `'hidden'` config array available on model to hide filters from URI when those are applied serverside:

```
class User extends Model {
    protected $distillery = [
        'hidden' => [
            'category' // - applied in controller; set from seo url
        ]
    ];
}
```

### Enable fallback to general filters

[](#enable-fallback-to-general-filters)

A model can use a general filter if one in it's namespace isn't defined:

```
class User extends Model {
    protected $distillery = [
        'fallback' => true
    ];
}
```

### API Filter-Pagination Route

[](#api-filter-pagination-route)

*Distillery comes with a standard filtering route, where you can filter/paginate any model automatically without attaching traits to models.*

This functionality **is disabled** by default. You need to enable it in the config.

Default route for filtering models is `/distill` use it in combination with model name and query string:

`/distill/{model}?page=1&limit=10&search=socks`

Models filterable by this route need to be added to the `distillery.routing.models` config array:

```
    'routing' => [

        'enabled' => true,

        'path' => 'distill',

        'middleware' => [
            'web',
        ],

        'models' => [
            'product' => App\Models\Product::class,
        ]
    ],
```

It's possible to change the route path in the config. If you want to protect it with Auth for example, you may also attach custom middleware to the route in config.

### Customizing pagination links

[](#customizing-pagination-links)

Pagination links are part of the Laravel pagination. Check out the [Laravel docs](https://laravel.com/docs/5.7/pagination#customizing-the-pagination-view) on how to customize them.

---

Examples
--------

[](#examples)

### Sorting

[](#sorting)

For sorting a model on multiple fields you would have a sort filter with values something like this: `sort=field-asc` and `sort=field-desc`:

```
php artisan distillery:filter Sort Product
```

```
class Sort implements Filter
{
    protected static $allowed = ['price', 'name', 'updated_at'];

    public static function apply(Builder $builder, $value)
    {
        if (Str::endsWith($value, ['-asc', '-desc'])) {
            [$field, $dir] = explode('-', $value);

            if (in_array($field, static::$allowed)) {
                return $builder->orderBy($field, $dir);
            }
        }

        return $builder->orderBy('updated_at', 'desc');
    }
}
```

And apply to apply the filter just add it to the qs: `/product-list?search=socks&sort=price-desc`.

### Filtering relations

[](#filtering-relations)

Sometimes you'll want to filter on relations of the model.

Suppose you have a Product model with multiple colors attached:

```
class Color implements Filter
{
    public static function apply(Builder $builder, $value)
    {
        $value = is_array($value) ? $value : [$value];
        $query = $builder->with('colors');

        foreach ($value as $colorId) {
            $query->whereHas('colors', function ($q) use ($colorId) {
                $q->where('id', $colorId);
            });
        }

        return $query;
    }
}
```

And to apply it: `/product-list?search=socks&sort=price-desc&color[]=2&color[]=5`.

Roadmap to 1.0.0
----------------

[](#roadmap-to-100)

- Add possibility to generate standard predefined filters (sort, search, ...).
- Make possible to define which paramateres to hide from url query strings.
- Add fallback to general filters that can be re-used across different models.
- Write tests.

License
-------

[](#license)

Laravel Distillery is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).

###  Health Score

23

—

LowBetter than 27% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity7

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity50

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

4

Last Release

2736d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/2760d6f86ba82642f42515eb70d92c275edcd1734a75ec54e39a96cc5ac86fad?d=identicon)[matejsvajger](/maintainers/matejsvajger)

---

Top Contributors

[![matejsvajger](https://avatars.githubusercontent.com/u/370837?v=4)](https://github.com/matejsvajger "matejsvajger (27 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/matejsvajger-laravel-distillery/health.svg)

```
[![Health](https://phpackages.com/badges/matejsvajger-laravel-distillery/health.svg)](https://phpackages.com/packages/matejsvajger-laravel-distillery)
```

###  Alternatives

[ruflin/elastica

Elasticsearch Client

2.3k50.4M203](/packages/ruflin-elastica)[opensearch-project/opensearch-php

PHP Client for OpenSearch

15024.3M65](/packages/opensearch-project-opensearch-php)[mailerlite/laravel-elasticsearch

An easy way to use the official PHP ElasticSearch client in your Laravel applications.

934529.3k2](/packages/mailerlite-laravel-elasticsearch)[massive/search-bundle

Massive Search Bundle

721.4M13](/packages/massive-search-bundle)[outl1ne/nova-multiselect-filter

Multiselect filter for Laravel Nova.

45802.7k3](/packages/outl1ne-nova-multiselect-filter)[handcraftedinthealps/zendsearch

a general purpose text search engine written entirely in PHP 5

39921.0k35](/packages/handcraftedinthealps-zendsearch)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
