denmarty/marty-query-filter
Composer 安装命令:
composer require denmarty/marty-query-filter
包简介
Reusable query filter pipeline for Laravel Eloquent builders.
README 文档
README
Reusable query filter pipeline for Laravel Eloquent builders.
Features
- simple
QueryFiltercontract for custom filters FilterPipelinewith Laravel container resolution- built-in
searchfilter - relation-aware filters through
whereHas(...) - normalized filter keys through
Str::snake(...) - support for inline filter configuration and legacy parameter registry
Requirements
- PHP 8.2+
- Laravel 11 or 12
Installation
composer require denmarty/marty-query-filter
Laravel package discovery is supported automatically.
Core idea
You register allowed filters once, then pass request data into the pipeline.
Each filter:
- has a public input key such as
status,auction_name,year_of_release - points to a class that implements
QueryFilter - can optionally receive extra constructor parameters
- can optionally be applied inside a relation
QueryFilter contract
Every custom filter must implement Denmarty\MartyQueryFilter\QueryFilter.
<?php namespace App\QueryFilters; use Denmarty\MartyQueryFilter\QueryFilter; use Illuminate\Database\Eloquent\Builder; final class StatusFilter implements QueryFilter { public function apply(Builder $query, mixed $value): Builder { if ($value === null || $value === '') { return $query; } return $query->where( column: 'status', operator: '=', value: $value, ); } }
Basic usage
<?php use App\Models\Post; use App\QueryFilters\StatusFilter; use Denmarty\MartyQueryFilter\FilterPipeline; $filters = new FilterPipeline( filters: [ 'status' => [ 'filter' => StatusFilter::class, ], ], ); $fields = [ 'status' => 'published', ]; $query = $filters->apply( query: Post::query(), fields: $fields, );
Preferred filter configuration format
The recommended format is to declare every filter as a configuration array:
<?php use App\QueryFilters\AuctionNameFilter; use App\QueryFilters\MileageFilter; use App\QueryFilters\YearOfReleaseFilter; use Denmarty\MartyQueryFilter\FilterPipeline; $filters = new FilterPipeline( filters: [ 'mileage' => [ 'filter' => MileageFilter::class, ], 'auction_name' => [ 'filter' => AuctionNameFilter::class, 'relation' => 'libAuctionName', ], 'year_of_release' => [ 'filter' => YearOfReleaseFilter::class, ], ], );
This format is explicit and keeps all filter metadata in one place.
Supported constructor arguments
FilterPipeline constructor accepts these named arguments:
filtersfilterParameterssearch
1. filters
Main filter registry.
Each filter must be declared as a configuration array with the filter key:
<?php use App\QueryFilters\StatusFilter; use Denmarty\MartyQueryFilter\FilterPipeline; $filters = new FilterPipeline( filters: [ 'status' => [ 'filter' => StatusFilter::class, ], ], );
Or with a relation:
<?php use App\QueryFilters\AuctionNameFilter; use Denmarty\MartyQueryFilter\FilterPipeline; $filters = new FilterPipeline( filters: [ 'auction_name' => [ 'filter' => AuctionNameFilter::class, 'relation' => 'libAuctionName', ], ], );
2. filterParameters
Legacy-compatible parameter registry. Still supported.
Use it when you want to keep filter class registration separate from extra parameters:
<?php use App\QueryFilters\AuctionNameFilter; use Denmarty\MartyQueryFilter\FilterPipeline; $filters = new FilterPipeline( filters: [ 'auction_name' => [ 'filter' => AuctionNameFilter::class, ], ], filterParameters: [ 'auction_name' => [ 'relation' => 'libAuctionName', ], ], );
3. search
Shortcut for configuring the built-in search filter directly in the constructor.
<?php use App\QueryFilters\StatusFilter; use Denmarty\MartyQueryFilter\FilterPipeline; $filters = new FilterPipeline( filters: [ 'status' => [ 'filter' => StatusFilter::class, ], ], search: [ 'fields' => ['title', 'slug'], 'relations' => [ 'author' => ['name'], 'category' => ['name'], ], ], );
Aliases are also supported:
search_fields->fieldssearch_relations->relations
Example:
<?php use App\QueryFilters\StatusFilter; use Denmarty\MartyQueryFilter\FilterPipeline; $filters = new FilterPipeline( filters: [ 'status' => [ 'filter' => StatusFilter::class, ], ], search: [ 'search_relations' => [ 'author' => ['name'], ], ], );
Full example
<?php use App\Models\Post; use App\QueryFilters\AuthorNameFilter; use App\QueryFilters\PublishedFilter; use App\QueryFilters\StatusFilter; use Denmarty\MartyQueryFilter\FilterPipeline; $filters = new FilterPipeline( filters: [ 'status' => [ 'filter' => StatusFilter::class, ], 'published' => [ 'filter' => PublishedFilter::class, ], 'author_name' => [ 'filter' => AuthorNameFilter::class, 'relation' => 'author', ], ], search: [ 'fields' => ['title', 'slug'], 'relations' => [ 'author' => ['name'], 'category' => ['name'], ], ], ); $fields = [ 'status' => 'published', 'author_name' => 'Denis', 'search' => 'Laravel', ]; $query = $filters->apply( query: Post::query(), fields: $fields, );
Relation-aware filters
If a filter configuration contains relation, the pipeline applies that filter through whereHas(...).
<?php use App\QueryFilters\AuctionNameFilter; use Denmarty\MartyQueryFilter\FilterPipeline; $filters = new FilterPipeline( filters: [ 'auction_name' => [ 'filter' => AuctionNameFilter::class, 'relation' => 'libAuctionName', ], ], );
This produces logic equivalent to:
<?php use App\QueryFilters\AuctionNameFilter; use Illuminate\Database\Eloquent\Builder; $query->whereHas('libAuctionName', function (Builder $relationQuery) use ($value): void { (new AuctionNameFilter())->apply( query: $relationQuery, value: $value, ); });
Inside the filter, you work with the relation query builder, not the root model query.
Built-in search filter
FilterPipeline automatically registers search.
That means you do not need to add search => SearchFilter::class manually.
Supported search configuration:
<?php use App\QueryFilters\StatusFilter; use Denmarty\MartyQueryFilter\FilterPipeline; $filters = new FilterPipeline( filters: [ 'status' => [ 'filter' => StatusFilter::class, ], ], search: [ 'fields' => ['title', 'slug'], 'relations' => [ 'author' => ['name'], 'author.profile' => ['first_name', 'last_name'], 'category' => ['name'], ], ], );
Search behavior
- empty search value is ignored
- if both
fieldsandrelationsare empty, search is ignored - relation search uses
orWhereHas(...) - relation entries with an empty field list are skipped
Important SQL note
The built-in SearchFilter uses ilike:
<?php $query->where( column: 'column', operator: 'ilike', value: '%term%', );
This is appropriate for PostgreSQL.
If your project uses MySQL or another database that does not support ilike, create your own search filter or override the package behavior for that project.
Container resolution
Filters are resolved through the Laravel container:
app()->make(...)app()->makeWith(...)
So constructor injection is supported.
<?php namespace App\QueryFilters; use Denmarty\MartyQueryFilter\QueryFilter; use Illuminate\Database\Eloquent\Builder; final class VisibilityFilter implements QueryFilter { public function __construct( private readonly string $column = 'visibility', ) {} public function apply(Builder $query, mixed $value): Builder { return $query->where( column: $this->column, operator: '=', value: $value, ); } }
Filter key normalization
Filter keys are normalized with Str::snake(...).
That means these keys resolve to the same filter:
auction_nameAuctionNameauctionName
Because of that, duplicate normalized keys are not allowed.
Validation rules enforced by the pipeline
The pipeline throws InvalidArgumentException when:
- a filter key is empty
- the same normalized filter key is registered twice
- a filter class does not exist
- a filter class does not implement
QueryFilter - inline filter configuration does not contain a valid
filterkey relationis present but not a non-empty stringfilterParameterscontains a key that is not registered infilters
Unknown input keys
Unknown keys from request input are ignored.
<?php use App\Models\Post; use App\QueryFilters\StatusFilter; use Denmarty\MartyQueryFilter\FilterPipeline; $pipeline = new FilterPipeline( filters: [ 'status' => [ 'filter' => StatusFilter::class, ], ], ); $query = $pipeline->apply( query: Post::query(), fields: [ 'status' => 'published', 'unknown_filter' => 'value', ], );
Only status will be applied.
Package structure
src/QueryFilter.php- filter contractsrc/FilterPipeline.php- registry, validation, executionsrc/SearchFilter.php- built-in text search filter
Testing
composer test
Formatting
composer lint
License
MIT
denmarty/marty-query-filter 适用场景与选型建议
denmarty/marty-query-filter 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 187 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 05 月 01 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「laravel」 「filters」 「pipeline」 「eloquent」 「query-filters」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 denmarty/marty-query-filter 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 denmarty/marty-query-filter 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 denmarty/marty-query-filter 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Simple filters for laravel
Pipeline
Livewire Filters is a series of Livewire components that provide you with the tools to do live filtering of your data from your own Livewire components.
A lightweight WordPress hook helper library. Register hooks before WordPress loads, run callbacks only once, and more.
Filters extension for Nette Framework
Laravel Advanced Filter
统计信息
- 总下载量: 187
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 7
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-05-01