定制 rebeccathedev/search-parser 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

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

rebeccathedev/search-parser

最新稳定版本:v0.3

Composer 安装命令:

composer require rebeccathedev/search-parser

包简介

A parser that converts a freeform query into an intermediate object, that can be converted to query many backends (SQL, ElasticSearch, etc).

README 文档

README

A powerful search query parser that transforms freeform search queries into structured objects for querying SQL databases, Eloquent models, or other backends.

✨ Features

  • 🎯 Parse Google-style search queries with field names, ranges, negation, and more
  • 🗄️ Built-in transforms for SQL (PDO) and Laravel Eloquent
  • 🔌 Extensible parser system for custom query types
  • 🔒 Field filtering and mapping for security
  • 🔎 Loose mode for fuzzy matching
  • 🚀 PHP 8.2+ with modern type safety

🚀 Quick Example

use RebeccaTheDev\SearchParser\SearchParser;

$parser = new SearchParser();
$query = $parser->parse('from:foo@example.com "bar baz" !meef date:2018/01/01-2018/08/01');

This tokenizes the search into a SearchQuery object with structured components:

RebeccaTheDev\SearchParser\SearchQuery Object
(
    [position:RebeccaTheDev\SearchParser\SearchQuery:private] => 0
    [data:protected] => Array
        (
            [0] => RebeccaTheDev\SearchParser\SearchQueryComponent Object
                (
                    [type] => field
                    [field] => from
                    [value] => foo@example.com
                    [firstRangeValue] =>
                    [secondRangeValue] =>
                    [negate] =>
                )

            [1] => RebeccaTheDev\SearchParser\SearchQueryComponent Object
                (
                    [type] => text
                    [field] =>
                    [value] => bar baz
                    [firstRangeValue] =>
                    [secondRangeValue] =>
                    [negate] =>
                )

            [2] => RebeccaTheDev\SearchParser\SearchQueryComponent Object
                (
                    [type] => text
                    [field] =>
                    [value] => meef
                    [firstRangeValue] =>
                    [secondRangeValue] =>
                    [negate] => 1
                )

            [3] => RebeccaTheDev\SearchParser\SearchQueryComponent Object
                (
                    [type] => range
                    [field] => date
                    [value] =>
                    [firstRangeValue] => 2018/01/01
                    [secondRangeValue] => 2018/08/01
                    [negate] =>
                )

            [4] => RebeccaTheDev\SearchParser\SearchQueryComponent Object
                (
                    [type] => text
                    [field] =>
                    [value] => #hashtag
                    [firstRangeValue] =>
                    [secondRangeValue] =>
                    [negate] =>
                )

        )

)

📦 Installation

composer require rebeccathedev/search-parser

Requirements: PHP 8.2+

No external dependencies required for core functionality. Eloquent transform requires illuminate/database.

📖 Usage

Basic Parsing

use RebeccaTheDev\SearchParser\SearchParser;

$parser = new SearchParser();
$query = $parser->parse('from:foo@example.com "exact phrase" !excluded');

The SearchQuery object is iterable:

foreach ($query as $component) {
    echo $component->type;  // 'field', 'text', 'range'
    echo $component->value;
}

🔧 Custom Parsers

Extend the parser by implementing the Parser interface:

use RebeccaTheDev\SearchParser\Parsers\Parser;
use RebeccaTheDev\SearchParser\SearchQueryComponent;

class HashtagParser implements Parser {
    public function parsePart(string $part): SearchQueryComponent {
        $component = new SearchQueryComponent();

        if (preg_match('!#(.*)!', $part, $match)) {
            $component->type = 'hashtag';
            $component->value = $match[1];
        }

        return $component;
    }
}

// Use it
$parser = new SearchParser();
$parser->addParser(new HashtagParser());
$query = $parser->parse('search #trending');

See src/Parsers/Hashtag.php for a working example. Note: parsers don't fall through - if your parser handles a part, processing moves to the next part.

🔄 Transforms

Transform parsed queries into SQL WHERE clauses or Eloquent query builders.

💾 SQL Transform

use RebeccaTheDev\SearchParser\Transforms\SQL\SQL;

$pdo = new PDO("sqlite:/tmp/database.db");
$transform = new SQL('default_field', $pdo);

$query = $parser->parse('from:foo@example.com "bar baz" !meef date:2018/01/01-2018/08/01');
$where = $transform->transform($query);

// Result:
// `from` = 'foo@example.com' and `default_field` = 'bar baz' and
// `default_field` != 'meef' and (`date` between '2018/01/01' and '2018/08/01')

✨ Eloquent Transform

use RebeccaTheDev\SearchParser\Transforms\Eloquent\Eloquent;

$users = User::query();
$transform = new Eloquent('name', $users);

$query = $parser->parse('status:active age:25-35');
$users = $transform->transform($query)->get();

🔎 Loose Mode

Enable fuzzy matching with LIKE queries:

$transform = new SQL('default_field', $pdo);
$transform->looseMode = true;
$where = $transform->transform($query);

// Result:
// `from` = 'foo@example.com' and `default_field` like '%bar baz%' and
// `default_field` not like '%meef%' and (`date` between '2018/01/01' and '2018/08/01')

🎨 Custom Component Transforms

Add custom transforms for your custom parsers:

use RebeccaTheDev\SearchParser\Transforms\SQL\Hashtag;

$pdo = new PDO("sqlite:/tmp/database.db");
$transform = new SQL('default_field', $pdo);
$transform->addComponentTransform(new Hashtag('default_field', $pdo));

$query = $parser->parse('search #trending');
$where = $transform->transform($query);

// Result: `default_field` = 'search' and `hashtag` = 'trending'

See src/Transforms/SQL/Hashtag.php for a working example.

🛡️ Filters

⚠️ Security Note

Important: The SQL transform escapes values but not field names. Always allowlist allowed fields before passing queries to transforms. Never trust user input for field names.

🎯 FieldFilter

Allowlist allowed fields for security:

use RebeccaTheDev\SearchParser\Filters\{Filter, FieldFilter};

$filter = new Filter();
$fieldFilter = new FieldFilter();
$fieldFilter->validFields = ['from', 'to', 'subject', 'date'];
$filter->addFilter($fieldFilter);

$query = $parser->parse('from:foo@example.com invalid:malicious subject:test');
$filtered = $filter->filter($query);

// Only 'from' and 'subject' fields are kept, 'invalid' is removed

🗺️ FieldNameMapper

Map user-facing field names to database column names:

use RebeccaTheDev\SearchParser\Filters\{Filter, FieldNameMapper};

$filter = new Filter();
$mapper = new FieldNameMapper();
$mapper->mappingFields = [
    'date' => 'created_at',
    'author' => 'user_id'
];
$filter->addFilter($mapper);

$query = $parser->parse('date:2024-01-01-2024-12-31 author:123');
$filtered = $filter->filter($query);

// 'date' becomes 'created_at', 'author' becomes 'user_id'

⚙️ Custom Filters

Implement the FiltersQueries interface:

use RebeccaTheDev\SearchParser\Filters\FiltersQueries;
use RebeccaTheDev\SearchParser\SearchQuery;

class MyCustomFilter implements FiltersQueries {
    public function filter(SearchQuery $query): SearchQuery {
        foreach ($query as $component) {
            // Your custom filtering logic
        }
        return $query;
    }
}

$filter = new Filter();
$filter->addFilter(new MyCustomFilter());

Useful SearchQuery methods:

  • remove(SearchQueryComponent $item) - Remove a component
  • replace(SearchQueryComponent $old, SearchQueryComponent $new) - Replace a component
  • merge(SearchQuery $query) - Merge two queries

🧪 Testing

composer install
./vendor/bin/phpunit

Some tests may be skipped if optional dependencies (like Eloquent) aren't installed.

📄 License

MIT License - see LICENSE file for details.

👩‍💻 Author

Made with 🩷 by Rebecca Peck

统计信息

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

GitHub 信息

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

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-12-13