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

Sphinxsearch Bundle Laravel Package

pet0r/sphinxsearch-bundle

Symfony bundle that integrates the Sphinx full-text search engine, providing configuration for indexers and a service layer for running searches from your application.

View on GitHub
Deep Wiki
Context7

Getting Started

Accept the framing first. This is a Symfony 2 bundle, not a Laravel package. For a Laravel developer the realistic "day-one" path is not composer require — it's reading the repository as a reference implementation, then wiring SphinxQL/Manticore by hand behind a repository interface. The steps below describe how you would actually use it as published, which only makes sense inside an existing Symfony 2.x app.

1. Install and register the bundle. Add it to composer.json and register it in app/AppKernel.php:

$bundles = array(
    // ...
    new Pet0r\SphinxsearchBundle\SphinxsearchBundle(),
);

There is no Flex recipe, no auto-discovery. If you vendored it manually, you likely also need a PSR-0/PSR-4 entry in app/autoload.php.

2. Make sure SphinxClient exists. The bundle is a thin wrapper around the SphinxClient class from sphinxapi.php. That class is not shipped with the bundle and is not reliably autoloaded from the sphinx extension. Either load the extension or require_once a vendored sphinxapi.php before the service is first resolved. The first symptom of failure is a Class 'SphinxClient' not found fatal at query time, not at container compile time.

3. Configure the connection and index aliases. Typical shape:

# app/config/config.yml
sphinxsearch:
    searchd:
        host: 127.0.0.1
        port: 9312
    indexes:
        products:
            index: products_main
        articles:
            index: articles_main

The logical-name → physical-index map is the most underrated part of the bundle: application code asks for products, ops can rename or split the physical index without touching PHP.

4. First use case — a search service call. Inject the configured search service into a controller or service and run a query against a logical index. This is the shape the bundle is designed to support (exact method names depend on the version — check Resources/config/services.xml for the service id and class):

$results = $this->get('sphinxsearch')->search('products', 'blue widget', ['limit' => 10]);

If you need SphinxQL (raw SQL-style queries, filtering, grouping), you are already past what this bundle gives you — drop to SphinxClient or PDO directly.

Where to look first in the source:

  • DependencyInjection/Configuration.php — the real config schema (this is your spec).
  • DependencyInjection/*Extension.php — how YAML becomes services.
  • Resources/config/services.xml — service ids you will inject.
  • Any Provider/Search/Indexer classes — the actual query surface.

5. On the Laravel side, if you are porting. Do not fork the bundle. Do this instead:

  • Copy the YAML config schema into config/search.php as a plain array.
  • Implement a SphinxClient-shaped service (or use the Manticore PHP client) as a container singleton.
  • Bind a SearchRepository interface with a SphinxRepository implementation.

That is the "getting started" that actually pays off.

Implementation Patterns

Inject, don't reach into the container. In Symfony 2 code, autowiring is unreliable — use service ids from services.xml in services.yml or constructor injection in your own classes. In the Laravel port, bind the client as a singleton and inject SearchRepository everywhere.

Wrap the search service in your own interface. This is the single highest-value pattern. The bundle's surface is small and versioned against an EOL framework; a one-method SearchInterface (e.g. search(string $index, string $query, array $opts): SearchResult) lets you swap Sphinx for Manticore or Scout later without touching call sites.

interface ProductSearch
{
    public function search(string $term, int $limit = 20): array;
}

final class SphinxProductSearch implements ProductSearch
{
    public function __construct(private SphinxClient $client) {}

    public function search(string $term, int $limit = 20): array
    {
        $this->client->SetMatchMode(SPH_MATCH_EXTENDED2);
        $this->client->SetLimits(0, $limit);
        return $this->client->Query($term, 'products_main')['matches'] ?? [];
    }
}

Query construction pattern. SphinxClient is a fluent-ish builder: SetMatchMode, SetFilter, SetSortMode, SetFieldWeights, SetLimits, then Query. Treat each call as building one request — the client is stateful and will carry filters into the next Query if you reuse an instance. Prefer a fresh client per logical query, or a documented ResetFilters/ResetGroupBy discipline.

Indexing workflow — plan the cron. The bundle exposes the query side; indexing is your operational responsibility. Standard pattern:

  1. Main index: rebuilt nightly from a batch source (DB dump / Eloquent chunk export) via indexer products_main --rotate.
  2. Delta index: a small companion index (products_delta) built frequently from rows where updated_at > last_index_at.
  3. Merge: periodic indexer --merge products_main products_delta --rotate.

The config map (products → products_main) lets application code stay stable while the main/delta/merge choreography runs underneath.

Result hydration pattern. SphinxClient::Query returns matches with the document id and attributes; the bundle does not hydrate into Eloquent models. The day-to-day pattern is: search returns IDs → fetch models with a single whereIn → reorder in PHP to match the search ranking (this ordering step is the one people forget).

$ids = array_column($matches, 'id');
$models = Product::whereIn('id', $ids)->get()->keyBy('id');
return collect($ids)->map(fn ($id) => $models[$id] ?? null)->filter()->values();

Integration tip — one service per logical domain. Rather than one godlike Search service, expose ProductSearch, ArticleSearch, etc., each bound to its physical index name. This mirrors how the config aliases work and keeps query options (match mode, field weights, limits) colocated with the domain they belong to.

Extension points worth knowing. The bundle's service definitions are the only real extension surface: override a service id in your app's services.yml to swap in a subclass with, say, SetConnectTimeout / SetReadTimeout tuning, or a client wrapper that logs slow queries. There is no event system inside the bundle to lean on.

Testing pattern. Because the client is a single injected object, replace it with a fake returning canned matches — do not hit searchd in unit tests. Reserve integration tests for a running searchd instance, guarded by an env flag.

Laravel-side port pattern (recommended). Replace the Symfony DI layer with a service provider; keep the config map shape identical; implement the repository against SphinxQL over PDO or the Manticore client. Provide an Artisan command (search:index, search:health) as the equivalent of the bundle's implicit "run the indexer" workflow.

Gotchas and Tips

The hard blockers, in order of how often they bite:

  • SphinxClient is not autoloaded. The bundle assumes the class is available; it is not guaranteed by any Composer dependency. Symptom: Class 'SphinxClient' not found on first query, not first boot. Fix: load the extension or require_once the vendored sphinxapi.php in a bootstrap file.
  • PHP and Symfony version constraints. Symfony 2 targets PHP 5.x/7.0-era. Composer resolution against PHP 8.1+ and modern Laravel is likely to fail before you write code; if it "works," it's untested. Do not attempt to run this on a Laravel 10/11 app — the framework primitives simply do not exist.
  • Zero stars / zero dependents. You are the first real user. No downstream bug-surface testing, no issue history to mine when something breaks. Budget for "you will debug this solo."
  • Upstream Sphinx is superseded. Since 2017, Manticore Search is the maintained fork. If you are choosing search today, build against Manticore and skip Sphinx proper; the bundle's concepts port cleanly.

Design quirks and pitfalls:

  • Stateful client. SphinxClient accumulates filters, sort modes, and limits between Query calls. If you inject a shared instance and call it from multiple services, filters leak across requests within the same PHP process. Mitigate with per-query instances or explicit resets.
  • Match mode defaults. SPH_MATCH_ALL is the historical default; if you want phrase/boolean semantics, set SPH_MATCH_EXTENDED2 explicitly. Silent scoring differences between environments usually trace back to this.
  • No result hydration. The bundle does not know your models. You own the ID-fetch-and-reorder step; forget it and you get results in ID order, not relevance order.
  • Config schema is the spec. DependencyInjection/Configuration.php is more accurate than the README. When the README and the code disagree (they will), trust the code.
  • Index name vs logical name confusion. Always pass the logical key (products) into your service layer and let config map to the physical index. Passing physical names directly couples application code to ops decisions and makes delta merges painful.
  • SphinxQL vs. SphinxAPI. The bundle uses the SphinxAPI protocol (port 9312), not SphinxQL (port 9306). If you want SphinxQL — filtering, aggregation, SQL-shaped queries — the bundle is the wrong layer, and searchd must be configured to expose port 9306.

Operational gotchas to instrument for from day one:

  • Silent index staleness. The cron stops or the delta merge fails; search returns plausible-but-old data with no error. Alert on updated_at lag or an explicit "last indexed at" heartbeat — this is the most common Sphinx incident.
  • Schema drift. Add a column to your model, forget to update the Sphinx index definition, and the field silently disappears (or truncates) from results. Generate the index schema from the model and diff it in CI.
  • Rebuild cost. A full reindex of a large corpus takes hours. Plan for --rotate (hot swap) and keep a known-good index as rollback.
  • Connection failures at boot, not at compile. A dead searchd surfaces on first query. Add a health-check command that opens the connection and runs a trivial Query, and run it in deploy smoke tests.

Where to extend, in order of least pain:

  1. Your own wrapper class around SphinxClient — set timeouts, log slow queries, add retry. Lowest-risk extension.
  2. Override a service id in your app's services.yml to swap in that wrapper. Reaches into the bundle's DI wiring, which is stable enough for this.
  3. Fork the bundle only if you genuinely need to change query construction or the config schema. At that point, extract the shape into a first-party package and let the fork go — do not carry it forward.

The debugging toolkit:

  • Turn on SphinxClient::SetArrayResult(true) to get predictable array-shaped results instead of the default nested-object shape — cuts a class of "why is matches shaped differently here" bugs.
  • Enable SetWarning/GetLastWarning and GetLastError after every Query; the client is quiet by default and warnings are the only signal for issues like "index not found, fell back to nothing."
  • Check searchd.log and query.log (enable query_log in searchd.conf) before suspecting the PHP side — most "wrong results" turn out to be a stale index or an unexpected SetFilter.
  • Diff the bundle's Configuration.php against your config.yml when a service fails to resolve — typos there fail opaquely.

Bottom line for a day-to-day user: the bundle is a thin, unmaintained wrapper over a superseded search daemon. If you are already running it in a Symfony 2 app, the useful discipline is to isolate it behind one interface, keep the logical-index indirection, and instrument index lag. If you are starting fresh, read the repository for the shape of the integration (logical index map, stateful query builder, ID-out-then-hydrate pattern) and implement that shape against SphinxQL or Manticore — do not consume the bundle.

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