loonpwn/laravel-swiftype
Laravel Swiftype integrates the Swiftype search API with your Laravel application, offering configuration and convenient helpers for indexing records, managing engines, and querying search results. Add fast, relevant site search with less boilerplate and familiar Laravel patterns.
This is a Laravel Scout driver that plugs Swiftype (now Elastic Site Search) in as your search engine, so you interact with it through the same Searchable trait and ::search() API you'd use with Algolia or Meilisearch.
Minimal setup:
composer require harlan-zw/laravel-swiftype
php artisan vendor:publish --tag="laravel-swiftype-config"
Add the Swiftype credentials to .env and point Laravel Scout at the new driver:
SCOUT_DRIVER=swiftype
SWIFTYPE_ENGINE=your-engine-slug
SWIFTYPE_API_KEY=your-api-key
// config/scout.php
'driver' => env('SCOUT_DRIVER', 'swiftype'),
Then make a model searchable and index it:
use Laravel\Scout\Searchable;
class Post extends Model
{
use Searchable;
public function toSearchableArray(): array
{
return [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
];
}
}
php artisan scout:import "App\Models\Post"
Where to look first: the package config file (engine slug, API key, index naming), then toSearchableArray() on your first model. The first real use case is almost always making site content searchable via a Post::search('term')->get() call — verify that end-to-end before wiring up more models.
2.0.3 note: this release only adds illuminate/support 9.x to the constraints. If you're on Laravel 9 you can now adopt it without a version conflict; nothing else changed behaviourally. If you're on Laravel 8 or below, 2.0.3 is a safe drop-in over 2.0.2.
Treat it as a plain Scout driver. Everything you already know about Scout works: Searchable trait, searchable()/unsearchable(), search()->paginate(), where() scopes, orderBy(), and scout:import/scout:flush. Write your app against Scout's contract, not Swiftype's, so swapping engines later is cheap.
Model observers do the syncing. With Searchable in place, create/update/delete on the model automatically pushes to Swiftype. For bulk operations, wrap them in Model::withoutSyncingToSearch(fn () => ...) or use searchable() on the collection rather than saving row by row — otherwise you generate one API call per model.
Custom field weighting lives in toSearchableArray(). This is the main lever you have. Shape the array to match the field weights you configure in the Swiftype dashboard — Swiftype applies boosts per field there, not in code, so keep the two in sync deliberately.
Queue the indexing in production. Set SCOUT_QUEUE=true so indexing happens on the queue. On a large import this is the difference between a request that times out and one that completes. Run a dedicated queue worker for the scout queue.
Use a custom engine name per environment. Configure SWIFTYPE_ENGINE to vary by APP_ENV (e.g. myapp-staging, myapp-production) so staging writes don't pollute production search results.
CI/testing. In tests, set SCOUT_DRIVER=null (or collection) so you're not hitting Swiftype on every test. Assert on toSearchableArray() output directly for shape, and keep a small number of integration tests that hit a dedicated staging engine.
Version constraint sensitivity. 2.0.3's sole change is broadening illuminate/support to ^9.0. If Composer still won't resolve, check your laravel/framework and laravel/scout versions — the support package isn't the only constraint in play. Pin ^2.0.3 explicitly rather than a loose ^2.0 if you need Laravel 9.
Engine must exist first. Swiftype requires the engine to be created in the dashboard before indexing; scout:import won't create it for you. A "not found" or 404 during import almost always means a wrong engine slug or a missing engine, not a package bug.
Index/document type naming. Scout derives document types from the model. If you rename a model or namespace, the old document type lingers and searches will split across both. Run scout:flush "App\Models\OldName" after renames.
Config cache trips people up. After changing config/scout.php or the package config, run php artisan config:clear — cached config is the most common reason a driver change appears to "do nothing."
toSearchableArray() must include an id. Swiftype needs a stable identifier to update/delete documents. Omit it and deletes silently fail to remove documents, leaving stale results.
Searching is eventually consistent. Swiftype indexes asynchronously; a document saved and immediately searched for may not appear. Don't write tests that assert searchability immediately after a save.
Debugging 4xx responses. Swiftype returns fairly terse errors. Enable HTTP logging (or wrap calls in a try/catch logging $e->getResponse()->getBody()) when a bulk import fails partway — the failing document is usually a field type mismatch (e.g. sending a number where the schema expects a string).
Extension point: if you need behaviour the driver doesn't expose, bind your own implementation of Scout's Engine contract in a service provider and delegate to the package's engine. That keeps Scout's public API intact while letting you add retries, logging, or custom query building.
How can I help you explore Laravel packages today?