vanry/laravel-scout-tntsearch
A Laravel Scout driver powered by TNTSearch that adds fast, local full-text search to Eloquent models. Supports fuzzy matching, boolean queries, geo search, custom tokenizers, and simple indexing without external search services.
This release (2.4.0) is a compatibility bump that adds support for Laravel Scout 9.0. There are no new APIs to learn — the day-to-day surface is the same Scout contract, so treat this as an upgrade-enablement release rather than a feature release.
composer require <vendor>/<package>:"^2.4" — 2.3.x and below are effectively locked out of Scout 9, so the constraint bump is the whole point of the release.composer show laravel/scout). If you're still on Scout 8, you can take 2.4.0 too, but there's no functional gain; the value is unblocking the Scout upgrade.php artisan vendor:publish --tag=<package>-config (tag name varies — check the package's service provider's publishes() array). Your driver config lands in config/scout.php alongside the built-in algolia/meilisearch entries.SCOUT_DRIVER=<package-driver-name> in .env — the driver string is registered by the package's service provider, and an unregistered/misspelled driver name fails at resolve time, not boot time.Laravel\Scout\Searchable to one small model and run php artisan scout:import "App\Models\YourModel". Verify the index round-trips with Model::search('term')->get() before touching larger models.Driver-swap pattern. Because this package plugs into Scout rather than wrapping it, the highest-leverage workflow is keeping the driver swappable. Keep engine-specific settings out of model classes and in config/scout.php; then SCOUT_DRIVER=collection in tests and CI gives you a zero-infrastructure path without changing any search call sites.
Model hook configuration. The usual customisation points are the ones you'll use weekly:
searchableAs() — return a per-tenant or per-environment index name (e.g. app_users_production). This is the cleanest way to isolate staging from production on a shared cluster.toSearchableArray() — project explicit fields. Returning $this->toArray() is the most common source of accidental PII in an index and of bloat.shouldBeSearchable() — gate on is_published / status so drafts never reach the index, instead of filtering at query time.searchableUsing() / makeAllSearchableUsing() — per-model driver and eager-load overrides; use the latter to with() relations inside toSearchableArray() so scout:import doesn't N+1.Queue-driven indexing. Indexing happens through Scout's observer events. Set SCOUT_QUEUE=true (or configure queue on the driver) so writes stay fast, and run a worker. For bulk data migrations, wrap with Model::withoutSyncingToSearch(fn () => ...) or Searchable::withoutSyncingToSearch() to avoid firing thousands of individual jobs, then scout:import once at the end.
Bulk import workflow. For large tables use php artisan scout:import with a chunked/queued variant if the package exposes one; otherwise raise memory_limit and chunk the underlying query in a custom command. Import is idempotent per document ID, so re-running after a mapping change is safe and is the standard fix for "stale index" incidents.
Integration tips.
deleted only if the model uses SoftDeletes and the driver handles it; verify that trashed records disappear rather than lingering.Scout::fake() for assertions in feature tests where supported by your Scout version, and collection driver for broader integration tests.Version constraints are the real breaking change. This release's only substantive change is the Scout 9 allowance. Check the package's composer.json: if it moved from ^8.0 to ^8.0|^9.0 you're fine, but if it moved to ^9.0 only, the upgrade is one-way and will force a Scout major bump on your app. Pin accordingly: "^2.3|^2.4" if you need a shared lockfile across apps on mixed Scout versions.
Scout 9 itself carries changes that surface here. Scout 9 dropped older Laravel/PHP floors. If your app was on Scout 8 and moves to 9, re-verify: searchableUsing() overrides, custom engines implementing Laravel\Scout\Engines\Engine, and anything calling Scout internals can break even though this package's public API didn't change. Grep for direct Engine implementations before upgrading.
Silent failure modes.
SCOUT_DRIVER string with soft deletion / SCOUT_DRIVER=null — searches return empty rather than erroring. Confirm which class the container resolves: app(\Laravel\Scout\EngineManager::class)->engine().toSearchableArray() returning a non-array or missing id → documents index but can never be updated or deleted.jobs table depth before debugging the engine.Debugging checklist, in order: (1) is the index populated — count documents directly against the engine; (2) does the document shape match toSearchableArray(); (3) is the driver resolving to the class you expect; (4) are queued jobs draining; (5) only then look at query syntax/analyzers.
Config quirks. Package config merges shallowly by default — nested arrays like settings.analysis are replaced wholesale if you override the parent key. Re-declare the whole subtree in config/scout.php rather than patching a single nested value, or use a deep merge in a service provider.
Extension points worth knowing. The engine is resolvable from the container, so you can decorate or swap it in a service provider for testing, request-scoped tenant index switching, or query logging. That's usually a better lever than forking the package — particularly since this release shows the maintainers track upstream Scout majors, so staying on the published driver keeps you on the supported path.
Upgrade verdict. Low risk, no migration needed, but zero upside unless you're moving to Scout 9. Schedule it together with the Scout major, not on its own; and run scout:import (or flush + re-import) after the upgrade since engine-level behaviour, not just PHP code, changed upstream.
How can I help you explore Laravel packages today?