Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Webman Scout Laravel Package

erikwang2013/webman-scout

Full-text search for Webman inspired by Laravel Scout. Add driver-based indexing and search to your models through a simple, familiar API, with support for popular search engines while keeping your existing model workflows intact.

View on GitHub
Deep Wiki
Context7

Getting Started

erikwang2013/webman-scout is a port of Laravel Scout to the Webman runtime, so the mental model carries over almost 1:1 — a Searchable trait on your models, a driver-based engine layer, and an index/import command. What changes is the host: there is no artisan, no service container bootstrapped the Laravel way, and no guarantee that model events or the Laravel queue facade are wired up the way Scout expects.

Minimal path to first result:

  1. composer require erikwang2013/webman-scout and confirm the engine adapter you actually want (Elasticsearch / Meilisearch / Algolia-style driver) is present in composer.json — Scout ports typically require the engine client as a separate dependency.
  2. Publish/copy the package config into config/plugin/erikwang2013/scout/ (Webman plugin convention) and set the driver plus host/credentials.
  3. Add the package's Searchable trait to one model and define toSearchableArray() explicitly rather than relying on toArray().
  4. Register the plugin in config/plugin/*/app.php (or config/plugin/erikwang2013/scout/app.php enable => true) and restart the process: php webman restart.
  5. Import: php webman scout:import "app\model\Post" — note the backslash-separated class string, which differs from the artisan invocation you're used to.
  6. Query: Post::search('keyword')->get().

First real use case: a single low-traffic read path (site search, admin list filtering) on one model. Get that green end-to-end before touching index settings, because most of the friction in this package is engine configuration, not the PHP API.

Implementation Patterns

Model layer. Keep toSearchableArray() explicit and flat. Anything you rely on for filtering (where('status', 1), whereIn('tags', ...)) must be a scalar or array field present in that array, not a relation loaded at query time. Use searchableAs() to pin index names so that renaming a class or moving namespaces doesn't orphan your index.

Bulk sync vs. event sync. This is the central decision. If model events aren't dispatched in your Webman bootstrapping (common — the Eloquent capsule needs an event dispatcher set explicitly), writes silently won't reach the index. Two safe patterns:

  • Observer/event sync — wire up an event dispatcher, verify with a single save() followed by a direct engine query, and only then trust it.
  • Explicit sync — call $model->searchable() / $model->unsearchable() from your service layer or controller after persistence. More verbose, zero ambiguity, and easier to reason about inside transactions.

For anything with bulk writes, prefer explicit sync and run scout:import on a schedule or as a post-deploy step.

Import and reindex workflow. Treat the index as a derived artifact:

php webman scout:flush "app\model\Post"
php webman scout:import "app\model\Post"

Chunk large imports (scout:import typically batches, but confirm the chunk size against your engine's bulk limits) and run them off-peak. For zero-downtime changes, point searchableAs() at a versioned index name (posts_v3), import fully, then flip the constant — far safer than an in-place reindex.

Query patterns. Stick to the portable subset: search(), where(), whereIn(), take(), paginate(), raw(). Hydration goes back through your model's query builder (whereKey), so results respect your connection but not your global scopes unless the package re-applies them — verify before assuming soft-deleted rows are excluded. Use ->raw() when you need engine-native scores or highlights, and ->keys() when you only need IDs to fan out into your own eager-loading query.

Queues. Don't assume Scout::queueUsing() works under Webman. If you want async indexing, prefer pushing searchable()/unsearchable() calls onto a Webman queue plugin job yourself, or drop a Redis pub/sub listener in front of the model writes. This is usually cleaner than trying to make the Laravel queue facade behave.

Integration tips. Register a thin service class (e.g. PostSearchService) that wraps search() + filters + pagination, so controllers never touch engine specifics. That single seam is also where you put the try/catch that degrades to a database LIKE query when the engine is down — search is rarely worth a 500.

Gotchas and Tips

  • v2.2.1 is a patch release with a changelog that only links a diff. There are no documented breaking changes, features, or deprecations, which usually means bugfixes/internal cleanup. Don't take that for granted: diff v2.2.0...v2.2.1 yourself, especially around the engine client version constraint in composer.json and any changes to the Searchable trait or config keys. Pin the version ("2.2.1") rather than ^2.2 until you've read the diff, and re-run your import on staging before rolling to production.
  • Elasticsearch mappings are inferred from the first document. If your first indexed record has a null field or an empty array, that field's type is locked in. A field that starts null can end up text and then reject numeric filters later. Fix is always: delete the index, re-import. Budget for this — it's the number-one time sink with this class of package.
  • ID type mismatches. Engines store _id as a string; hydration goes back via whereKey(). With an integer primary key you can get "found N results, hydrated 0 models." Cast explicitly in toSearchableArray() or in the hydration path.
  • toSearchableArray() defaults to toArray(). Under Webman that can pull in relations you didn't intend to index and balloon document size. Always override it.
  • No config:cache, no artisan. Config changes need a php webman restart; long-running worker processes will happily serve stale engine credentials until you do.
  • Silent no-op writes. If nothing is being indexed, check in this order: is the trait actually on the model; is the event dispatcher set on the Eloquent capsule; is the driver name in config correct; does a manual $model->searchable() work. Manual searchable() succeeding while save() does nothing is the classic signature of missing event wiring.
  • Chinese/CJK text needs an analyzer. With Elasticsearch, use ik_max_word/ik_smart or a CJK analyzer on text fields. Default standard analyzer tokenizes per-character and gives poor relevance.
  • shouldBeSearchable() is your filter for partial indexing. Cheap way to keep drafts and soft-deleted rows out of the index without touching your query layer.
  • Extension points: the engine contract is the seam — implement a custom driver when you need a non-shipped backend, and a custom Searchable trait (extending the packaged one) when you need to change indexing behavior globally, e.g. adding a tenant ID to every document and forcing it into every query.
  • Debugging tip: when search() returns nothing, bypass the package. Hit the engine directly (curl the index's _search endpoint, or the Meilisearch /indexes/{uid}/search) and confirm the documents exist and are shaped as expected. That instantly splits "indexing problem" from "query-building problem."
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource