PHPackages                             dev-astro/laravel-query-filters - 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. [Database &amp; ORM](/categories/database)
4. /
5. dev-astro/laravel-query-filters

ActiveLibrary[Database &amp; ORM](/categories/database)

dev-astro/laravel-query-filters
===============================

A simple and powerful query filtering system for Laravel

v1.0.0(1mo ago)201MITPHPPHP ^8.1

Since Jun 23Pushed 1mo agoCompare

[ Source](https://github.com/AhmedEzz20/laravel-query-filters)[ Packagist](https://packagist.org/packages/dev-astro/laravel-query-filters)[ RSS](/packages/dev-astro-laravel-query-filters/feed)WikiDiscussions main Synced 2w ago

READMEChangelog (1)Dependencies (2)Versions (2)Used By (0)

Laravel Query Filters
=====================

[](#laravel-query-filters)

A simple and powerful query filtering system for Laravel.

This package helps you build clean, reusable, and scalable query filters for your Eloquent models with support for:

- Dynamic filters
- Date range filtering
- Sorting system
- Clean architecture

---

Requirements
------------

[](#requirements)

- PHP `^8.1`
- Laravel `10.x | 11.x | 12.x`

---

Installation
------------

[](#installation)

```
composer require dev-astro/laravel-query-filters
```

The package uses Laravel's auto-discovery — no manual registration needed.

---

How It Works
------------

[](#how-it-works)

The package works by mapping request query string keys to method names in your filter class.

For every key in the `$filters` array, it checks if the request contains it, then calls the matching `camelCase` method in your filter class with the value.

```
Request key  →  Method name
search_name  →  searchName($value)
category_id  →  categoryId($value)

```

Keys ending in `_from` and `_to` are handled automatically as date range filters (no method needed).

---

Features
--------

[](#features)

- Artisan command to generate filter classes (`make:filter`)
- Automatic `Filterable` trait registration via auto-discovery
- Dynamic filters via `$filters` whitelist
- Date range filtering (`_from` / `_to` convention)
- Built-in sorting (`order_by` / `order_dir`) with `$sortable` whitelist
- `perPage` support via query string
- Lightweight — no config file needed

---

Step-by-step Usage
------------------

[](#step-by-step-usage)

### 1. Add the `Filterable` Trait to Your Model

[](#1-add-the-filterable-trait-to-your-model)

```
use DevAstro\LaravelQueryFilters\Traits\Filterable;

class Brand extends Model
{
    use Filterable;

    protected $filter = BrandFilter::class;
}
```

The trait adds:

- A `filter()` Eloquent scope that auto-resolves your filter class
- Support for `?perPage=20` in the request to override pagination size

---

### 2. Generate a Filter Class

[](#2-generate-a-filter-class)

```
php artisan make:filter BrandFilter
```

This creates `app/Http/Filters/BrandFilter.php`:

```
namespace App\Http\Filters;

use DevAstro\LaravelQueryFilters\Contracts\BaseFilters;

class BrandFilter extends BaseFilters
{
    protected array $filters = [
        //
    ];

    protected function example($value)
    {
        return $this->builder;
    }
}
```

---

### 3. Define Your Filters

[](#3-define-your-filters)

Add filter keys to the `$filters` array and write a matching `camelCase` method for each:

```
class BrandFilter extends BaseFilters
{
    protected array $filters = [
        'search_name',
        'status',
        'created_from',   // handled automatically — no method needed
        'created_to',     // handled automatically — no method needed
    ];

    protected array $sortable = [
        'id',
        'name',
        'created_at',
    ];

    protected function searchName($value)
    {
        return $this->builder->where('name', 'like', "%{$value}%");
    }

    protected function status($value)
    {
        return $this->builder->where('status', $value);
    }
}
```

---

### 4. Use in Your Controller

[](#4-use-in-your-controller)

```
public function index()
{
    return BrandResource::collection(
        Brand::filter()->paginate()
    );
}
```

That's it. The `filter()` scope automatically picks up the current request and applies all matching filters.

---

Built-in Features
-----------------

[](#built-in-features)

### Date Range (`_from` / `_to`)

[](#date-range-_from--_to)

Any key ending in `_from` or `_to` is automatically handled as a `whereDate` filter — no method needed:

```
protected array $filters = [
    'created_from',  // WHERE DATE(created_at) >= ?
    'created_to',    // WHERE DATE(created_at)  The column name is derived by stripping the suffix: `created_from` → `created` column... so name your keys as `{column}_from` / `{column}_to`. For example, `published_from` filters on the `published` column.

Example request:

```
GET /api/brands?created_from=2024-01-01&created_to=2024-12-31

```

---

### Sorting (`order_by` / `order_dir`)

[](#sorting-order_by--order_dir)

Sorting is handled automatically — you don't need to add `order_by` or `order_dir` to your `$filters` array. Just define which columns are allowed in `$sortable`:

```
protected array $sortable = [
    'id',
    'name',
    'created_at',
];
```

- `order_dir` defaults to `asc` if not provided or invalid
- If `order_by` column is not in `$sortable`, the sort is silently ignored (security)

Example requests:

```
GET /api/brands?order_by=name&order_dir=asc
GET /api/brands?order_by=created_at&order_dir=desc

```

---

### Pagination (`perPage`)

[](#pagination-perpage)

The `Filterable` trait overrides `getPerPage()`, so you can control page size from the request:

```
GET /api/brands?perPage=20
GET /api/brands?perPage=50&page=2

```

Falls back to the model's default (`15`) if not provided.

---

Full Example
------------

[](#full-example)

**Request:**

```
GET /api/brands?search_name=nike&created_from=2024-01-01&order_by=name&order_dir=desc&perPage=20

```

**Filter class:**

```
namespace App\Http\Filters;

use DevAstro\LaravelQueryFilters\Contracts\BaseFilters;

class BrandFilter extends BaseFilters
{
    protected array $filters = [
        'search_name',
        'created_from',
        'created_to',
    ];

    protected array $sortable = [
        'id',
        'name',
        'created_at',
    ];

    protected function searchName($value)
    {
        return $this->builder->where('name', 'like', "%{$value}%");
    }
}
```

**Model:**

```
use DevAstro\LaravelQueryFilters\Traits\Filterable;

class Brand extends Model
{
    use Filterable;

    protected $filter = BrandFilter::class;
}
```

**Controller:**

```
public function index()
{
    return BrandResource::collection(
        Brand::filter()->paginate()
    );
}
```

**Equivalent SQL:**

```
SELECT * FROM brands
WHERE name LIKE '%nike%'
  AND DATE(created_at) >= '2024-01-01'
ORDER BY name DESC
LIMIT 20
```

---

Writing Custom Filters
----------------------

[](#writing-custom-filters)

Every filter method receives the request value and has access to `$this->builder`:

```
// Exact match
protected function status($value)
{
    return $this->builder->where('status', $value);
}

// Relationship filter
protected function categoryId($value)
{
    return $this->builder->where('category_id', $value);
}

// Range filter (manual)
protected function minFollowers($value)
{
    return $this->builder->where('followers_count', '>=', $value);
}

// Existence check (boolean flag)
protected function hasProducts($value)
{
    return $this->builder->has('products');
}

// whereIn
protected function statuses($value)
{
    return $this->builder->whereIn('status', explode(',', $value));
}
```

---

Quick Reference
---------------

[](#quick-reference)

Request ParameterBehaviorNeeds method?`?search_name=x`calls `searchName($value)`✅ Yes`?created_from=date``whereDate(column, '>=', date)`❌ No`?created_to=date``whereDate(column, '
