philiprehberger/laravel-search-query-parser 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

philiprehberger/laravel-search-query-parser

Composer 安装命令:

composer require philiprehberger/laravel-search-query-parser

包简介

Parse GitHub-style search queries into structured filters for Laravel Eloquent. Supports field:value, comparisons, exclusions, and relation filters.

README 文档

README

Tests Latest Version on Packagist Last updated

Parse GitHub-style search queries into structured filters for Laravel Eloquent. Supports field:value, comparisons, exclusions, and relation filters.

Requirements

  • PHP 8.2+
  • Laravel 11 or 12

Installation

composer require philiprehberger/laravel-search-query-parser

The service provider is registered automatically via Laravel's package auto-discovery.

Quick Start

use PhilipRehberger\SearchQueryParser\QueryParser;

$parser = new QueryParser();
$parsed = $parser->parse('design status:active amount:>1000 -archived');

$parsed->textSearch;    // "design"
$parsed->filters;       // [['field' => 'status', 'operator' => 'equals', 'value' => 'active'], ...]
$parsed->excludeTerms;  // ["archived"]

$parsed->hasTextSearch();    // true
$parsed->hasFilters();       // true
$parsed->hasExcludeTerms();  // true
$parsed->isEmpty();          // false

You can also resolve QueryParser from the container or use the facade:

// Via facade
use PhilipRehberger\SearchQueryParser\Facades\SearchQueryParser;

$parsed = SearchQueryParser::parse('status:active');

// Via dependency injection
public function __construct(private QueryParser $parser) {}

Syntax Reference

Syntax Example Description
keyword design Plain keyword — goes into textSearch
"phrase" "web design" Quoted phrase — treated as a single text search term
field:value status:active Exact match filter (equals operator)
field:>value amount:>1000 Greater than comparison
field:<value date:<2026-01-01 Less than comparison
field:>=value total:>=500 Greater than or equal
field:<=value hours:<=40 Less than or equal
field:!=value status:!=archived Not equal comparison
field:v1,v2,v3 status:active,pending Match any of (comma-separated → in operator)
-keyword -archived Exclude keyword — goes into excludeTerms
has:relation has:invoices Has related records
no:relation no:projects Has no related records

Notes

  • Longer operators (>=, <=, !=) are checked before shorter ones (>, <, =) to prevent partial matches.
  • URLs (https://..., http://...) are never treated as field filters.
  • Field names must match /^[a-zA-Z_][a-zA-Z0-9_]*$/ — digits-first tokens fall through to text search.
  • has: and no: prefix matching is case-insensitive.

Security: Text operators (ContainsOperator, StartsWithOperator, EndsWithOperator, NotContainsOperator) automatically escape LIKE wildcards (% and _) in user input to prevent wildcard injection.

Operator Reference

Text Operators

Class Label Input Type Requires Value
ContainsOperator contains text yes
NotContainsOperator does not contain text yes
EqualsOperator equals text yes
NotEqualsOperator does not equal text yes
StartsWithOperator starts with text yes
EndsWithOperator ends with text yes
IsEmptyOperator is empty text no
IsNotEmptyOperator is not empty text no

Numeric Operators

Class Label Input Type
GreaterThanOperator greater than number
GreaterOrEqualOperator greater than or equal number
LessThanOperator less than number
LessOrEqualOperator less than or equal number
BetweenOperator between range
NotBetweenOperator not between range

BetweenOperator and NotBetweenOperator accept a value of ['min' => x, 'max' => y] or [x, y].

Date Operators

Class Label Input Type Requires Value
DateEqualsOperator on date date yes
DateBeforeOperator before date yes
DateAfterOperator after date yes
DateBetweenOperator between dates daterange yes
DateInLastOperator in the last duration yes
DateInNextOperator in the next duration yes
IsTodayOperator is today text no
IsThisWeekOperator is this week text no
IsThisMonthOperator is this month text no

DateInLastOperator and DateInNextOperator accept ['amount' => int, 'unit' => 'days|weeks|months|years'].

DateBetweenOperator accepts ['start' => date, 'end' => date] or [date, date].

Array Operators (JSON columns)

Class Label Input Type
InOperator is any of multiselect
NotInOperator is not any of multiselect
HasAnyOperator has any of multiselect
HasAllOperator has all of multiselect

HasAnyOperator and HasAllOperator use whereJsonContains for JSON array columns.

Relation Operators

Class Label Requires Value
HasRelationOperator has no
HasNotRelationOperator does not have no
HasCountOperator has count yes

HasCountOperator accepts ['operator' => '>=', 'count' => 1].

Usage with Eloquent

The ParsedQuery DTO gives you structured data you can apply to your queries however you like. Here is a typical pattern:

use PhilipRehberger\SearchQueryParser\QueryParser;
use PhilipRehberger\SearchQueryParser\Operators\Text\ContainsOperator;
use PhilipRehberger\SearchQueryParser\Operators\Text\EqualsOperator;
use PhilipRehberger\SearchQueryParser\Operators\Numeric\GreaterThanOperator;
use PhilipRehberger\SearchQueryParser\Operators\Array\InOperator;
use PhilipRehberger\SearchQueryParser\Operators\Relation\HasRelationOperator;
use PhilipRehberger\SearchQueryParser\Operators\Relation\HasNotRelationOperator;

$parser = new QueryParser();
$parsed = $parser->parse($request->input('q', ''));

$query = Project::query();

// Apply free-text search
if ($parsed->hasTextSearch()) {
    $term = $parsed->textSearch;
    $query->where(function ($q) use ($term) {
        $q->where('name', 'like', "%{$term}%")
          ->orWhere('description', 'like', "%{$term}%");
    });
}

// Apply field filters
$operatorMap = [
    'equals'        => new EqualsOperator(),
    'in'            => new InOperator(),
    'greater_than'  => new GreaterThanOperator(),
    'has'           => new HasRelationOperator(),
    'has_not'       => new HasNotRelationOperator(),
];

foreach ($parsed->filters as $filter) {
    $operator = $operatorMap[$filter['operator']] ?? null;
    if ($operator) {
        $operator->apply($query, $filter['field'], $filter['value']);
    }
}

// Apply exclusion terms
foreach ($parsed->excludeTerms as $term) {
    $query->where('name', 'not like', "%{$term}%");
}

$projects = $query->get();

Build / Round-trip

QueryParser::build() serializes a ParsedQuery back into a query string. This is useful for storing canonical search state or passing queries between requests.

$parsed = $parser->parse('design status:active -archived');

// Modify the parsed query...
$built = $parser->build($parsed);
// "design status:active -archived"

Syntax Help

getSyntaxHelp() returns all supported syntax patterns, suitable for rendering a help tooltip or autocomplete:

$help = $parser->getSyntaxHelp();
// [
//   ['syntax' => 'keyword',       'example' => 'design',           'description' => 'Search for keyword in all fields'],
//   ['syntax' => '"phrase"',      'example' => '"web design"',     'description' => 'Search for exact phrase'],
//   ['syntax' => 'field:value',   'example' => 'status:active',    'description' => 'Filter by specific field'],
//   ...
// ]

ParsedQuery DTO

readonly class ParsedQuery
{
    public string $textSearch;
    public array  $filters;      // array<{field: string, operator: string, value: mixed}>
    public array  $excludeTerms; // array<string>

    public function hasTextSearch(): bool;
    public function hasFilters(): bool;
    public function hasExcludeTerms(): bool;
    public function isEmpty(): bool;
    public function toArray(): array;
}

API

QueryParser

Method Description
parse(string $query): ParsedQuery Parse a query string into a structured ParsedQuery DTO
build(ParsedQuery $parsed): string Serialize a ParsedQuery back into a query string
getSyntaxHelp(): array Return all supported syntax patterns for tooltips or autocomplete

ParsedQuery DTO

Property / Method Type Description
$textSearch string Free-text portion of the query
$filters array Structured filters: [{field, operator, value}]
$excludeTerms array<string> Terms prefixed with -
hasTextSearch(): bool Whether a free-text term is present
hasFilters(): bool Whether any field filters are present
hasExcludeTerms(): bool Whether any exclusion terms are present
isEmpty(): bool Whether the query produced no results
toArray(): array Serialize to array

Development

composer install
vendor/bin/phpunit
vendor/bin/pint --test
vendor/bin/phpstan analyse

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT

philiprehberger/laravel-search-query-parser 适用场景与选型建议

philiprehberger/laravel-search-query-parser 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 77 次下载、GitHub Stars 达 1, 最近一次更新时间为 2026 年 03 月 06 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「parser」 「search」 「filter」 「query」 「laravel」 「eloquent」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 philiprehberger/laravel-search-query-parser 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 philiprehberger/laravel-search-query-parser 我们能提供哪些服务?
定制开发 / 二次开发

基于 philiprehberger/laravel-search-query-parser 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 77
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 13
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-03-06