PHPackages                             reynotech/laravel-query-builder-custom - 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. [API Development](/categories/api)
4. /
5. reynotech/laravel-query-builder-custom

ActiveLibrary[API Development](/categories/api)

reynotech/laravel-query-builder-custom
======================================

custom

076PHP

Since Feb 24Pushed 4mo ago1 watchersCompare

[ Source](https://github.com/reynotech/laravel-query-builder-custom)[ Packagist](https://packagist.org/packages/reynotech/laravel-query-builder-custom)[ RSS](/packages/reynotech-laravel-query-builder-custom/feed)WikiDiscussions main Synced today

READMEChangelogDependenciesVersions (1)Used By (0)

ReynoTECH Laravel Query Builder Custom
--------------------------------------

[](#reynotech-laravel-query-builder-custom)

Extensions and helpers for `spatie/laravel-query-builder`:

- Advanced string/number/date filters with consistent operators
- Query definition helpers (`HasQueryDefinition`)
- Distinct values pagination (`DistinctValuesQueryBuilder`)
- Selectable collections for UI payloads
- Date and Mongo date casts

### Requirements

[](#requirements)

- PHP 8.2+
- `spatie/laravel-query-builder` ^6

### Installation

[](#installation)

```
composer require reynotech/laravel-query-builder-custom
```

### Features Overview

[](#features-overview)

- **Advanced filters** for string, number, and date with common operator keys.
- **Query definition DSL** for filters and sorts (plus addons).
- **Distinct values API** via cursor pagination and optional filtering.
- **Selectable collections** to transform models into select-friendly payloads.
- **Casts** for dates and MongoDB `UTCDateTime` with configurable formats.

Query Definitions (HasQueryDefinition)
--------------------------------------

[](#query-definitions-hasquerydefinition)

Use the `HasQueryDefinition` trait to define filters/sorts once and reuse them when building `QueryBuilder`.

### Option A: `queryFilters` + `queryAddons`

[](#option-a-queryfilters--queryaddons)

```
use ReynoTECH\QueryBuilderCustom\Filters\StringAdvancedFilter;
use ReynoTECH\QueryBuilderCustom\Traits\HasQueryDefinition;

class Client extends Model
{
    use HasQueryDefinition;

    public function queryFilters(callable $table): array
    {
        return [
            'name' => [
                'filter' => StringAdvancedFilter::class,
                'sort' => true,
                'internal' => $table('name'),
            ],
            'status' => [
                'filter' => StringAdvancedFilter::class,
                'internal' => $table('status'),
            ],
        ];
    }

    public function queryAddons(callable $table): array
    {
        return [
            'extra' => [
                'role' => [
                    'filter' => StringAdvancedFilter::class,
                    'internal' => $table('role'),
                ],
            ],
        ];
    }
}
```

Build filters/sorts:

```
use Spatie\QueryBuilder\QueryBuilder;

[$filters, $sorts] = Client::tableQueryDefinitionFiltersNew('*');

$builder = QueryBuilder::for(Client::class)
    ->allowedFilters($filters)
    ->allowedSorts($sorts);
```

Notes:

- Addon `dates` is included by default and adds `created_at`/`updated_at` using `DateFilter` (configurable).
- `HasQueryDefinition::bootHasQueryDefinition()` sets the array delimiter using `query_builder_custom.has_query_definition.array_value_delimiter` (default `|`), so `filter[name]=op|value` maps to `['op','value']`.
- `$table()` helps resolve internal table/field names:
    - `$table()` current table
    - `$table('field')` current table, different field
    - `$table('field', Other::class)` another table

### Option B: `queryDefinition`

[](#option-b-querydefinition)

If you prefer a single array:

```
public function queryDefinition(): array
{
    return [
        'filters' => ['id', 'status'],
        'sorts' => ['id'],
        'addons' => [
            'dates' => [
                'filters' => ['created_at'],
                'sorts' => ['created_at'],
            ],
        ],
    ];
}
```

Use `tableQueryDefinition`, `tableQueryDefinitionAll`, or `tableQueryDefinitions()` as needed.

Filters
-------

[](#filters)

Filters expect values in the form `filter[field]=op|value` (using the `|` array delimiter).
For `in` and `bw`, use comma-separated lists (configurable via `query_builder_custom.filters.separator`). Global operator aliases can be configured via `query_builder_custom.filters.operator_aliases`.

### StringAdvancedFilter

[](#stringadvancedfilter)

Operators:

- `eq`, `neq`
- `con`, `ncon` (contains / not contains)
- `bw`, `ew` (begins / ends with)
- `nbw`, `new` (not begins / not ends with)
- `e`, `ne`, `missing` (null/empty checks)
- `in`

Examples:

- `filter[name]=con|Ali`
- `filter[name]=in|Alice,Bob`
- `filter[note]=missing|`

JSON path columns (`meta->label`) are supported and wrapped/cast for `LIKE` and raw checks.

### NumberAdvancedFilter

[](#numberadvancedfilter)

Operators:

- `eq`, `neq`
- `lt`, `lte`, `gt`, `gte`
- `bw` (between, comma-separated)
- `in`

Examples (separator defaults to comma `,`):

- `filter[score]=gte|10`
- `filter[score]=bw|10,20`
- `filter[numbers->value]=eq|7`

JSON path columns are cast to `DECIMAL(20, 6)` for comparisons.

### DateFilter

[](#datefilter)

Operators:

- `eq`, `neq`
- `lt`, `lte`, `gt`, `gte`
- `bw` (between, comma-separated)
- `in`
- `null`, `nnull`

Examples (separator defaults to comma `,`):

- `filter[event_date]=eq|10/02/2026`
- `filter[event_date]=bw|10/02/2026,15/02/2026`
- `filter[event_date]=in|10/02/2026,15/02/2026`

Date parsing uses `config('app.date_format_solo', 'd/m/Y')`.

Distinct Values Pagination
--------------------------

[](#distinct-values-pagination)

`HasQueryDefinition` overrides the model builder to `DistinctValuesQueryBuilder`, enabling a distinct-values API.

Usage:

```
Model::query()->hasDistinctValues();
```

Request parameters:

- `_dist` = field name to get distinct values for
- `_fdist` = optional filter value (uses the same filterer)
- `_dcur` = cursor name for pagination

Example:

```
GET /clients?_dist=status&_fdist=con|act

```

You can customize:

```
Model::query()->hasDistinctValues(
    preQuery: fn($q) => $q->where('active', 1),
    filterer: \ReynoTECH\QueryBuilderCustom\Filters\StringAdvancedFilter::class
);
```

Selectable Collections
----------------------

[](#selectable-collections)

Use the `Selectable` trait to provide select-friendly data from Eloquent collections.

```
use ReynoTECH\QueryBuilderCustom\Traits\Selectable\Selectable;

class Client extends Model
{
    use Selectable;

    public function selector(Model $model): array
    {
        return ['id' => $model->id, 'name' => $model->name];
    }

    public function selectorFoo(Model $model): array
    {
        return ['value' => 'foo-' . $model->name];
    }

    public function selectorSingle(Model $model): string
    {
        return $model->name;
    }

    public function selectorSingleFoo(Model $model): string
    {
        return 'foo-' . $model->name;
    }

    public function content(Model $model): array
    {
        return [$model->id => $model->name];
    }
}
```

Collection helpers:

- `toSelect()` / `toSelect('foo')`
- `toSelectSingle()` / `toSelectSingle('foo')`
- `toSelectArray()`
- `toDataContentAttribute()`

Pagination helper:

```
Client::query()->toSelectPaginate(15);
```

Returns:

```
[
  'data' => Collection,
  'has_more' => bool,
  'per_page' => int,
  'current_page' => int,
]
```

Casts
-----

[](#casts)

Date and MongoDB date casts with configurable formats.

### DateCast / DateTimeCast

[](#datecast--datetimecast)

```
use ReynoTECH\QueryBuilderCustom\Casts\DateCast;
use ReynoTECH\QueryBuilderCustom\Casts\DateTimeCast;

protected $casts = [
    'date' => DateCast::class,
    'published_at' => DateTimeCast::class,
];
```

Constants:

- `DateCast::SETPARSED`
- `DateCast::SETONLYMONTHYEAR`
- `DateTimeCast::TOONLYDATE`

Formats:

- `app.date_format_solo` (default `d/m/y`)
- `app.date_format` (default `d/m/y h:i A`) Optional cast overrides:
- `query_builder_custom.casts.date.*`
- `query_builder_custom.casts.datetime.*`

### MongoDateCast / MongoDateTimeCast

[](#mongodatecast--mongodatetimecast)

MongoDB `UTCDateTime` equivalents:

- `MongoDateCast::SETPARSED`
- `MongoDateCast::SETONLYMONTHYEAR`
- `MongoDateTimeCast::TOONLYDATE`Optional cast overrides:
- `query_builder_custom.casts.mongo.*` (merged with date/datetime settings)

Testing
-------

[](#testing)

```
composer test
```

MySQL-backed tests (set `MYSQL_*` or `DB_*` env vars):

```
composer test:mysql
```

Configuration
-------------

[](#configuration)

Set optional overrides:

```
config([
    'query_builder_custom.filters.delimiter' => '|',
    'query_builder_custom.filters.separator' => ',',
    'query_builder_custom.filters.operator_aliases' => [
        // 'contains' => 'con',
        // 'before' => 'lt',
    ],
]);
```

Default values live in `config/query_builder_custom.php`.

Publish the config in Laravel:

```
php artisan vendor:publish --tag=query-builder-custom-config
```

###  Health Score

20

—

LowBetter than 13% of packages

Maintenance52

Moderate activity, may be stable

Popularity9

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity12

Early-stage or recently created project

 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.

### Community

Maintainers

![](https://www.gravatar.com/avatar/5a54a0525bf964f79d90d7402dda39b9661c559f3b239a970eb122850c95b66a?d=identicon)[reynotech](/maintainers/reynotech)

---

Top Contributors

[![reynotech](https://avatars.githubusercontent.com/u/24636036?v=4)](https://github.com/reynotech "reynotech (15 commits)")

### Embed Badge

![Health badge](/badges/reynotech-laravel-query-builder-custom/health.svg)

```
[![Health](https://phpackages.com/badges/reynotech-laravel-query-builder-custom/health.svg)](https://phpackages.com/packages/reynotech-laravel-query-builder-custom)
```

###  Alternatives

[exsyst/swagger

A php library to manipulate Swagger specifications

35916.4M7](/packages/exsyst-swagger)[hubspot/api-client

Hubspot API client

24016.2M20](/packages/hubspot-api-client)[pocketmine/bedrock-protocol

An implementation of the Minecraft: Bedrock Edition protocol in PHP

172445.0k16](/packages/pocketmine-bedrock-protocol)[botman/driver-telegram

Telegram driver for BotMan

93459.5k6](/packages/botman-driver-telegram)

PHPackages © 2026

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