rosh1ck/sphinxsearch-bundle
Sphinxsearch Bundle integrates Sphinx full-text search into PHP applications, offering configuration and services to index and query data. Use it to connect your app to Sphinx and add fast, scalable search.
Reality check first: this is a Symfony bundle — Symfony2, with release 0.2.5 merely adding Symfony3 support. It ships a SphinxsearchBundle class extending Symfony\Component\HttpKernel\Bundle\Bundle, a DI extension, and a service definition for the Sphinx client — none of which Laravel will autoload or register. Symfony 2 went EOL in 2018 and Symfony 3 in 2021; the bundle has had no meaningful feature work since ~2020. So "getting started" for a Laravel developer is really: extract the query-side wrapper, skip the bundle.
Step 1 — Install what you'll actually use. Add the underlying Sphinx PHP client rather than the bundle:
composer require sphinxsearch/sphinxsearch
Then read DependencyInjection/Configuration.php and Resources/config/services.xml from the bundle repo on GitHub to see the config keys (host, port, indexes) and how the client is constructed. That's the only part you'll port.
Step 2 — Stand up Sphinx/Manticore separately. The bundle does not run the search daemon; it only talks to it. Install searchd (or better, Manticore Search, the maintained fork), write sphinx.conf with a source pointing at your Laravel DB, and run indexer --all once to prove data flows.
Step 3 — First use case: search a products table. Register a singleton in AppServiceProvider:
$this->app->singleton(SphinxClient::class, function () {
$c = new SphinxClient();
$c->setServer(config('sphinx.host', '127.0.0.1'), config('sphinx.port', 9312));
$c->setMatchMode(SPH_MATCH_EXTENDED2);
return $c;
});
Then $client->query('iphone', 'products_index') and inspect $result['matches'] (doc IDs + weights). Look first at the bundle's Sphinxsearch.php/service class — it's ~100 lines wrapping exactly this.
Two-connection model. Sphinx exposes two ports: 9312 (native binary API, what this bundle uses) and 9306 (SphinxQL, MySQL wire protocol). SphinxQL is often easier in Laravel because you can reuse the PDO/query builder:
DB::connection('sphinx')->select("SELECT id, WEIGHT() AS w FROM products_index WHERE MATCH(?) LIMIT 20", [$query]);
Both are valid; the bundle only covers 9312. Pick one and stay consistent.
Indexing is cron, not request-time. The bundle has no indexer. Pattern:
indexer products_index --rotate # cron every N minutes
Use delta indexes (a delta source with sql_query_range over updated_at) plus a main index merged periodically, otherwise a full reindex on a large table blocks you.
Hydrate IDs back into Eloquent and preserve rank. Sphinx returns only id and weight. Standard flow:
$ids = array_column($result['matches'], 'id');
$models = Product::whereIn('id', $ids)->get()
->sortBy(fn($m) => array_search($m->id, $ids))->values();
Filters beat post-filtering. Push price, category_id, in_stock into the Sphinx config as attributes and use $client->setFilter('category_id', [$id]). Do not fetch 10k rows and WHERE them in PHP.
Pagination contract. $client->setLimits($offset, $perPage) and read $result['total_found'] (capped by max_matches, default 1000) — that's your paginator total, not count($matches).
Excerpts for search-result snippets. Call buildExcerpts($docs, 'products_index', $query, ['before_match' => '<em>', 'after_match' => '</em>']). It needs the full text column, so keep a content/name field indexed and fetch it alongside IDs.
Laravel integration tip: if you genuinely want the bundle's API, run Symfony's DI container standalone via symfony/dependency-injection and load the bundle's Extension — note 0.2.5 targets the Symfony 3 container API, so you'd need the Symfony 3.x component line, not 2.x. But realistically, copy the ~80 lines into App\Services\SphinxSearch and add it to a service provider. That's the whole bundle.
SphinxClient), 9306 = SphinxQL. Connecting the PHP client to 9306 fails with a protocol error that looks like a daemon crash.Symfony3), not a Laravel bridge: no ServiceProvider, no config merge, no facade, and the Bundle base class doesn't exist in a Laravel app. Don't composer require this expecting php artisan support — extract the wrapper.SphinxClient construction are unchanged. Nothing in your ported service class needs to change, and it still carries no Sphinx client or protocol improvements.max_matches silently truncates. Default 1000 — deep pagination returns total_found that ignores anything past it. Set max_matches explicitly or switch to SphinxQL LIMIT/OFFSET with max_matches in config.SPH_MATCH_EXTENDED2 parses @, ~, |, ", -. A raw $request->q can throw or produce garbage. Use $client->escapeString() (the bundle exposes it) and wrap phrases yourself.charset_table, min_word_len, and morphology = stem_en must match between index and searchd, and the daemon must be restarted (not just reindexed) after config changes. Symptom: a term that works in indexer --all returns zero hits in queries.buildExcerpts needs the index to exist and the field to be in sql_field_string/sql_attr_*. Passing the wrong field name returns false, not an exception — always check the return.weight is relative within a result set; don't compare weights across queries. Tune with setFieldWeights(['title' => 10, 'body' => 1]).indexer --rotate is atomic, but a --all without --rotate leaves the daemon serving the old index until restarted. Automate --rotate and add searchd --status to your healthcheck.sphinxsearch/sphinxsearch, and put Manticore in your Docker compose so your team can reproduce bugs locally.How can I help you explore Laravel packages today?