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

Sphinx Search Laravel Package

scorpio/sphinx-search

A lightweight Laravel package that brings Sphinx full-text search to your Eloquent models. Define and sync indexes, then query them with a fluent SphinxQL builder using familiar syntax, keeping search fast and easy to maintain in any PHP application.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a thin object-oriented wrapper around the PECL sphinx extension's SphinxClient class — it is not a Sphinx server, an indexer, or a Laravel integration. Before writing any application code, confirm you can actually run it:

  1. Check the environment first. You need the sphinx PECL extension loaded (php -m | grep sphinx) and a running searchd instance. The repository is archived and targets an older PHP/Sphinx generation, so on a modern Laravel stack (PHP 8.2+, Herd/Sail/Docker images) you will likely have no prebuilt extension. If class_exists('SphinxClient') is false, stop here — nothing else works.

  2. Read composer.json and src/ before the README. The README is the only documentation; the class surface is small enough to read directly. Identify the client wrapper class and how it exposes the underlying SphinxClient (constructor args for host/port, setters for index/match/filters).

  3. Install and bind. composer require scorpio/sphinx-search, then register a singleton in AppServiceProvider with host/port from config/sphinx.php (host, port, indexes, and a enabled flag so local dev without Sphinx degrades gracefully rather than fataling).

  4. First use case — ID-then-hydrate search over one table. Define a Sphinx source in sphinx.conf pointing at your posts table, run indexer --all, then query from a tinker session:

    $client = app(SphinxClientWrapper::class);
    $results = $client->search('laravel eloquent', 'posts_index'); // returns ids + metadata
    $posts = Post::whereIn('id', $results->ids())->get();
    

    Get that round trip working end to end before building abstractions — it exercises the extension, the daemon, the config, and the config reload all at once.

If any of step 1 fails, the pragmatic "getting started" is to evaluate Manticore Search (the maintained Sphinx fork, SphinxQL/HTTP compatible) or Laravel Scout with Meilisearch/Typesense instead.

Implementation Patterns

Keep it behind an interface. The value of this package is that it hides SphinxClient's procedural API; preserve that by defining your own SearchService contract (search(SearchQuery): SearchResult) and treating the package as one implementation. When you migrate to Manticore or Scout, only the implementation changes.

Configuration in a config file, not constructors. Publish a config/sphinx.php holding host, port, index names, max_matches, and timeouts, and bind the client as a singleton. Because the underlying client is stateful (filters, sort mode, limits persist between calls), prefer a new client instance or an explicit reset per query rather than reusing one across requests in a queue worker.

Query object, then translate. Build a small immutable SearchQuery value object (terms, filters, sort, offset/limit, excerpt flags) in your application layer, and a single translator that maps it onto the package's setters. This is where you centralise escaping, filter ranges, and SetSortMode, and it is the piece that makes the whole thing unit-testable without a live daemon.

Indexing is out of scope — own it explicitly. The package only queries. Your pipeline is: Sphinx source reads MySQL → indexer --rotate on a schedule or after writes (queued job, debounced) → searchd serves. For near-real-time needs, use a delta index + --merge; do not expect the package to help here. Schedule indexing via Schedule::command(...) in routes/console.php / Kernel.

Hydration and ordering. Search returns ranked IDs, not models. Fetch with whereIn, then re-sort in PHP using the returned order ($ids → array_flip → usort), and page in the search layer (offset/limit on the search, not paginate() on the Eloquent query). Wrap this in a SearchResult that carries ids, total_found, time, and words.

Excerpts/snippets are one of the few real reasons to use this over a naive LIKE query. If you need highlighted snippets, either enable stored fields in the Sphinx source or fetch the source documents and call the snippet builder on the hydrated models.

Testing. Extract the translation logic and assert on the query state without Sphinx; for integration, run the daemon in CI behind a container and skip the suite when the extension is absent (markTestSkipped), so contributors on modern PHP aren't blocked.

Gotchas and Tips

  • The extension is the real dependency risk. SphinxClient is a PECL extension last meaningfully updated for PHP 5/7. There is no Composer-installable fallback, so the package is unusable on many modern environments regardless of its own quality. Treat "can I load the extension?" as a go/no-go gate.
  • Archived and zero-adoption. No dependents, no issue activity, no security response. Vendor it into app/Support/ or fork it if you commit to Sphinx; don't build architecture that assumes upstream fixes.
  • No Laravel integration whatsoever. No service provider, facade, config publish, or Scout driver — expect to write all the glue yourself. This is fine, but budget for it rather than discovering it.
  • Stateful client, stale filters. Filters, sort mode, SetLimits, and SetFilterRange persist on the client until reset. In long-running workers (Octane, Horizon) this produces results that mysteriously inherit the previous query's constraints. Instantiate fresh per query or reset explicitly in a finally.
  • Query syntax injection. The match string goes through Sphinx's extended query syntax (@field, |, -, ""). Raw user input with @, -, or quotes will change semantics or error. Escape reserved characters or use SetMatchMode deliberately; never interpolate unescaped input.
  • Charset and tokenisation mismatches are the top "why do I get zero results" cause: charset_table in sphinx.conf, the connection charset, and your DB collation must agree. Also verify min_word_len and stopwords — short or common terms vanish silently.
  • Silent partial failures. Errors surface via the client's last-error/last-warning accessors rather than exceptions. Check both after every call in development, and log them; a failed connection typically returns an empty result set rather than throwing.
  • Ordering lost after hydration, and total_found vs. returned count diverge under max_matches (default ~1000). Set max_matches and cutoff explicitly if pagination depth matters.
  • Debugging loop: run searchd --console in the foreground, query with the CLI search tool to isolate server-vs-PHP problems, then indexer --all --rotate and check the daemon picks up the new index. Most "stale results" reports are a forgotten rotate.
  • Extension points worth adding yourself: an enabled config flag with a null-object implementation, a SearchService interface for swapping engines, and a decorator that logs slow/failed queries. Given the package's age, plan the Manticore Search exit path (SphinxQL over HTTP) from day one — the query-object layer you build around this package ports over almost unchanged.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package