erikwang2013/webman-scout
Composer 安装命令:
composer require erikwang2013/webman-scout
包简介
Scout-style full-text search for Webman, Laravel, ThinkPHP, and Hyperf (Eloquent models, multiple engines).
README 文档
README
Navigate (top): Top · Features · Framework support · Requirements · Installation · Framework-specific setup · Configuration · Model setup · Basic usage · Advanced builder · Artisan / Webman commands · Queues · Builder reference · References · License
Left navigation (outline)
webman-scout
Driver-based full-text search for Eloquent models, inspired by Laravel Scout and shopwwi/webman-scout. This fork adds time ranges, aggregations, OpenSearch, vector / geo helpers, and clearer multi-framework configuration.
Languages: this file is English (default). 简体中文
Features
- Scout-like API for easy migration from Laravel Scout / shopwwi webman-scout
- Engines: OpenSearch, Elasticsearch, Meilisearch, Typesense, Algolia, XunSearch, Database, Collection, Null
- OpenSearch-first advanced queries: aggregations, facets, KNN, geo distance
- Optional queue-driven indexing (Webman Redis Queue when available)
- Index settings sync, soft deletes, chunked import
Framework support
Runtime integration targets applications that expose Laravel’s config() helper and an Illuminate container (app()), with Eloquent (Illuminate\Database\Eloquent\Model) models.
| Framework | Versions | Notes |
|---|---|---|
| Webman | 1.x / 2.x | Default install: plugin config under config/plugin/erikwang2013/webman-scout/. |
| Laravel | 7.x – 11.x | Copy the plugin app.php array into config/scout.php (or config/erikwang2013.webman-scout.php) and set SCOUT_CONFIG_KEY (see below). Requires PHP 8.0+ (Laravel 7 on PHP 8 is supported in recent 7.x releases). |
| Hyperf | 2.x – 3.x | Use Hyperf’s config + DI; Hyperf\Database\Model is Eloquent-compatible. Map Scout options into config and set SCOUT_CONFIG_KEY if not using the Webman plugin path. |
| ThinkPHP | 6.x / 8.x | Use when the app loads Illuminate config / app() (e.g. hybrid setups or illuminate/database Eloquent models). Native think\Model is not wired to the Searchable trait; call engine APIs manually or use Eloquent models for indexed entities. |
Composer requires illuminate/* ^7.0 – ^11.0 and symfony/console ^5.4 – ^7.0 so dependency resolution matches your framework stack.
Requirements
- PHP ^8.0
- Eloquent models for the
Searchabletrait illuminate/bus,contracts,database,http,pagination,queue,support(versions aligned with your Laravel / Hyperf / ThinkPHP stack)
Installation
composer require erikwang2013/webman-scout
The Composer autoload files entry loads helpers.php, which defines app(), event(), and scout_config() when needed and registers EngineManager.
Webman
After install, run the plugin installer (copies config and queue consumers):
- Config:
config/plugin/erikwang2013/webman-scout/ - Consumers:
app/queue/redis/search/
Laravel / Hyperf / ThinkPHP (non-plugin layout)
- Copy the contents of
src/config/plugin/erikwang2013/webman-scout/app.phpinto your application config, e.g.config/scout.php, returning the same associative array (keys:driver,prefix,opensearch,meilisearch, …). - Set environment variable
SCOUT_CONFIG_KEY=scout(no trailing dot) so lookups useconfig('scout.driver'), etc., instead of the Webman plugin path. - Ensure your bootstrap registers Illuminate’s
configrepository and container soconfig()andapp()resolveEngineManagerand engine clients.
If SCOUT_CONFIG_KEY is unset, the package prefers config('plugin.erikwang2013.webman-scout.app') when that array exists; otherwise it tries scout or erikwang2013.webman-scout.
Framework-specific setup
The following sections assume composer require erikwang2013/webman-scout is already done.
Webman (1.x / 2.x)
- Enable the plugin in your Webman project (per Webman plugins) so that
config/plugin/erikwang2013/webman-scout/is published. If your stack runs the packageInstallstep, it copies plugin config and queue consumers; otherwise copy fromvendor/erikwang2013/webman-scout/src/config/plugin/erikwang2013/webman-scout/into your project. - Config lives at
config/plugin/erikwang2013/webman-scout/app.php. You normally do not setSCOUT_CONFIG_KEYsoscout_config()resolves this path automatically. - Console: commands are registered via
config/plugin/erikwang2013/webman-scout/command.php(e.g.php webman scout:import "App\\Model\\Product"— adjust namespace to your app). - Models usually extend
support\Model(Eloquent-based) anduse Searchable. - Queues (optional): install/configure webman/redis-queue, set
'queue' => truein Scout config, and run consumers underapp/queue/redis/search/(scout_make,scout_remove). If Redis Queue is missing orqueueis false, indexing runs synchronously in the request/process.
Laravel (7.x – 11.x)
-
Config file: add
config/scout.phpthatreturns the same structure as this package’ssrc/config/plugin/erikwang2013/webman-scout/app.php(keys:driver,prefix,opensearch,meilisearch,queue, …).- Avoid loading both
laravel/scoutand this package under the sameconfig/scout.phpunless you know how to separate them; this package is a standalone Scout-style implementation.
- Avoid loading both
-
Environment: in
.envsetSCOUT_CONFIG_KEY=scoutsoscout_config('driver')readsconfig('scout.driver'). -
Container:
helpers.phpregistersEngineManager(and Meilisearch client when installed) on the activeapp()container. If you bootstrap before Composer’sfilesautoload, register the same bindings inAppServiceProvider::register():$this->app->singleton(\Erikwang2013\WebmanScout\EngineManager::class, function ($app) { return new \Erikwang2013\WebmanScout\EngineManager($app); });
-
Artisan commands: commands are Symfony
Commandclasses with names likescout:import. Register them with the framework:-
Laravel 10 and below — in
app/Console/Kernel.php:protected $commands = [ \Erikwang2013\WebmanScout\Command\ImportCommand::class, \Erikwang2013\WebmanScout\Command\FlushCommand::class, \Erikwang2013\WebmanScout\Command\IndexCommand::class, \Erikwang2013\WebmanScout\Command\DeleteIndexCommand::class, \Erikwang2013\WebmanScout\Command\DeleteAllIndexesCommand::class, \Erikwang2013\WebmanScout\Command\QueueImportCommand::class, \Erikwang2013\WebmanScout\Command\SyncIndexSettingsCommand::class, ];
-
Laravel 11+ — in
bootstrap/app.phpuse->withCommands([...])with the same class list (see Laravel 11 structure).
-
-
Models extend
Illuminate\Database\Eloquent\Model(or your base model) anduse Searchable. -
Queues: async indexing in this package is wired to Webman\RedisQueue when that class exists. On stock Laravel, keep
'queue' => falsein Scout config so changes are applied synchronously, or implement your own pipeline (e.g. dispatch a Laravel job from model observers) usingsyncMakeSearchable/ engineupdate()as a reference.
Hyperf (2.x – 3.x)
- Config: place the Scout array under a Hyperf config file, e.g.
config/autoload/scout.php, returning the same keys as the packageapp.php. SetSCOUT_CONFIG_KEY=scoutin the environment Hyperf reads (soconfig('scout')is the root array). config()/app(): Hyperf providesconfig(); ensure the IlluminateContaineris the one returned byapp()if you rely on packagehelpers.php, or bindEngineManagerin a HyperfConfigProvider/ dependency injection config pointing at your container bridge.- Models:
Hyperf\Database\Modelis Eloquent-compatible — useSearchablethe same way as on Laravel when the database component is configured. - Console: register the same command classes as Laravel with Hyperf’s command system (or invoke Symfony
Applicationwith these commands in a custom entry script). - Queues: same as Laravel — without
Webman\RedisQueue, preferqueue=> false or custom async jobs.
ThinkPHP (6.x / 8.x)
- Scope: the
Searchabletrait expects Eloquent (Illuminate\Database\Eloquent\Model) observers and collections. It does not attach tothink\Modelout of the box. - When it works: projects that already use
illuminate/database(or another stack) with real Eloquent models, or a bridge that exposes Laravel-styleconfig()andapp()with the Illuminate container, can follow the Laravel steps: Scout config file +SCOUT_CONFIG_KEY+EngineManagerbinding. - Pure ThinkPHP models: index data by calling
app(EngineManager::class)->engine()(or the concrete engine class)update/delete/searchwith arrays you build yourself, or maintain a thin Eloquent model mapped to the same table for search-only usage. - Config: ThinkPHP’s
config('scout.driver')works if you defineconfig/scout.php(or the version your major version uses) with the same array shape as this package’sapp.php.
Quick checklist
| Step | Webman | Laravel | Hyperf | ThinkPHP (Eloquent/hybrid) |
|---|---|---|---|---|
| Scout config file | config/plugin/.../app.php |
config/scout.php |
config/autoload/scout.php |
config/scout.php (or equivalent) |
SCOUT_CONFIG_KEY |
Usually omit | scout |
scout |
scout (if not using plugin path) |
| Console | php webman scout:* |
php artisan scout:* (after registration) |
Hyperf command registration | Per your console setup |
| Async indexing | Redis Queue + consumers | queue false or custom jobs |
queue false or custom jobs |
queue false or custom jobs |
Configuration
All Scout options are read via scout_config('key'), which respects the resolved config root above.
| Key | Purpose |
|---|---|
driver |
Default engine: opensearch, elasticsearch, meilisearch, typesense, algolia, database, collection, null, … |
prefix |
Index name prefix |
queue |
Enable async indexing (Webman Redis Queue when installed) |
chunk.searchable / chunk.unsearchable |
Chunk sizes for bulk import/remove |
soft_delete |
Keep soft-deleted rows in the index |
OpenSearch example
'opensearch' => [ 'host' => getenv('OPENSEARCH_HTTP_HOST') ?: 'https://127.0.0.1:6205', 'username' => getenv('OPENSEARCH_USERNAME') ?: 'admin', 'password' => getenv('OPENSEARCH_PASSWORD') ?: 'admin', 'prefix' => getenv('OPENSEARCH_INDEX_PREFIX') ?: '', 'ssl_verification' => (bool) (getenv('OPENSEARCH_SSL_VERIFICATION') ?: false), 'indices' => [ 'products' => [ 'settings' => [ /* ... */ ], 'mappings' => [ 'properties' => [ 'vector' => ['type' => 'knn_vector', 'dimension' => 1536], 'location' => ['type' => 'geo_point'], ], ], ], ], ],
Model setup
use Erikwang2013\WebmanScout\Searchable; use support\Model; // Webman; use your Eloquent base otherwise class Product extends Model { use Searchable; public function searchableAs(): string { return 'products'; } public function toSearchableArray(): array { return [ 'id' => $this->id, 'title' => $this->title, 'content' => $this->content, 'price' => $this->price, 'created_at' => $this->created_at?->timestamp, 'location' => ['lat' => $this->lat, 'lon' => $this->lng], 'vector' => $this->embedding ?? [], ]; } public function searchableFields(): array { return ['title', 'content']; } }
Basic usage
// Search $products = Product::search('phone')->get(); $products = Product::search('phone', function ($builder) { $builder->where('status', 1); })->get(); $paginator = Product::search('phone')->paginate(15); Product::search('keyword') ->where('status', 1) ->whereIn('category_id', [1, 2, 3]) ->orderBy('created_at', 'desc') ->limit(20) ->get(); // Indexing $product->searchableSync(); $product->searchable(); $product->unsearchable(); Product::makeAllSearchable(); Product::removeAllFromSearch(); Product::withoutSyncingToSearch(function () { Product::query()->where('id', 1)->update(['title' => 'New title']); });
Advanced builder (OpenSearch / Elasticsearch-oriented)
Product::search('') ->whereRange('created_at', ['gte' => 1609459200, 'lte' => 1640995200], true) ->whereRange('price', ['gte' => 100, 'lt' => 500]) ->get(); Product::search('') ->whereGeoDistance('location', 31.23, 121.47, 10.0) ->get(); Product::search('') ->fulltextSearch('keyword', ['title', 'content'], ['operator' => 'and']) ->get(); Product::search('') ->orderByVectorSimilarity([0.1, -0.2 /* ... */], 'vector') ->get(); $builder = Product::search('keyword') ->aggregate('price_ranges', 'range', 'price', ['ranges' => [ ['from' => 0, 'to' => 100], ['from' => 100, 'to' => 500], ]]) ->facet('category_id', ['size' => 10]); $results = $builder->get(); $aggregations = $builder->getAggregations(); $facets = $builder->getFacets(); $engine = app(\Erikwang2013\WebmanScout\EngineManager::class)->engine(); $engine->updateIndexMappings('products', [ 'properties' => [ 'new_field' => ['type' => 'keyword'], ], ]); $builder = Product::search('keyword'); $builder->whereRange('created_at', $range)->get(); $builder->clearAdvancedConditions();
Artisan / Webman commands
On Webman, use php webman …. On Laravel, register the command classes (see Laravel under Framework-specific setup) and run php artisan scout:import, etc.
| Command | Description |
|---|---|
php webman scout:import [Model] |
Full import; --chunk, --fresh |
php webman scout:flush [Model] |
Clear model data from the index |
php webman scout:delete-index [Model] |
Drop index for the model |
php webman scout:index |
List / create indexes (engine-dependent) |
php webman scout:queue-import |
Queue-based import |
php webman scout:sync-index-settings |
Sync index settings |
php webman scout:delete-all-indexes |
Delete all managed indexes (dangerous) |
Use --help on each command for options.
Queues
With queue enabled, searchable() / unsearchable() dispatch to Webman Redis Queue when Webman\RedisQueue\Redis is available. Ensure consumers under app/queue/redis/search are running (e.g. scout_make, scout_remove).
Builder reference (extensions)
| Method | Description |
|---|---|
whereRange($field, array $range, bool $inclusive = true) |
Range filter |
whereGeoDistance($field, $lat, $lng, $radius) |
Geo distance |
fulltextSearch($query, array $fields = [], array $options = []) |
Full-text |
orderByVectorSimilarity(array $vector, ?string $vectorField = null) |
Vector sort |
aggregate(...) / facet(...) |
Aggregations / facets |
addResultProcessor(callable $processor) |
Post-process hits |
getAggregations() / getFacets() |
Read facet/agg results |
clearAdvancedConditions() |
Reset advanced state |
Engines such as OpenSearch expose updateIndexMappings(string $index, array $mappings).
References
开源不易,欢迎支持
| 微信 | 支付宝 |
|---|---|
![]() |
![]() |
License
MIT
© erik erik@erik.xyz · https://erik.xyz
erikwang2013/webman-scout 适用场景与选型建议
erikwang2013/webman-scout 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 300 次下载、GitHub Stars 达 3, 最近一次更新时间为 2026 年 01 月 28 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「search」 「elasticsearch」 「laravel」 「opensearch」 「algolia」 「thinkphp」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 erikwang2013/webman-scout 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 erikwang2013/webman-scout 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 erikwang2013/webman-scout 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
A Laravel package to retrieve data from Google Search Console
Indexed Search Autocomplete - Extends the TYPO3 Core Extension Indexed_Search searchform with an autocomplete feature.
Abstraction Layer to index and search entities
simple api library.
Pimcore 10.x Website Indexer (powered by Zend Search Lucene)
Symfony bundle for Elasticsearch integration with round-robin load balancing
统计信息
- 总下载量: 300
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 3
- 点击次数: 31
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-01-28

