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

search/sphinxsearch-bundle

A Symfony bundle that integrates Sphinx full-text search, offering configuration for indexes and client services so PHP applications can run fast search queries with minimal setup. Useful for adding scalable, configurable search to Symfony projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm what you're integrating with. This bundle is a Symfony 2-era wrapper around the Sphinx Search daemon (2.x), talking to it over the SphinxQL protocol (the MySQL-compatible port, default 9306). It is not an ORM, not a crawler, and not a replacement for Doctrine — it queries an index that Sphinx built from your SQL data. Before touching PHP, stand up searchd and confirm you can hit it: mysql -h127.0.0.1 -P9306 -e "SHOW TABLES;" should list your indexes.

Install and register. Add the package to composer.json and register the bundle in app/AppKernel.php. Register it in prod (and any environment where searchd actually exists) rather than unconditionally in dev/test, otherwise every developer needs a running Sphinx instance just to boot the app.

// app/AppKernel.php
$bundles = array(
    // ...
    new Timewasted\SearchSphinxSearchBundle\SearchSphinxSearchBundle(),
);

Where to look first. The README is thin, so treat the code as the documentation, in this order:

  1. DependencyInjection/Configuration.php — the authoritative schema for the config tree (key names drift between versions; do not trust blog posts).
  2. DependencyInjection/*Extension.php and Resources/config/*.xml — the service IDs the bundle registers and the constructor arguments (host/port/index map).
  3. The main service class — read the signature of its search/excerpt methods. You want to know: does it return raw arrays, does it build SphinxQL for you, how does it escape the query, and does it expose SHOW META?
  4. Tests/ if present — usually the clearest usage examples.

Find the runtime service ID rather than guessing:

php app/console container:debug | grep -i sphinx

First use case — the standard "search then hydrate" loop. Configure one index that mirrors a Doctrine entity, then wire a controller that searches it and loads the entities by ID:

# app/config/config.yml (verify exact keys against Configuration.php)
search_sphinx_search:
    host: 127.0.0.1
    port: 9306
    indexes:
        product:
            name: product_main      # the index name inside searchd
            fields: [title, description, sku]
            attributes: [category_id, price, in_stock, created_at]
$rows = $sphinx->search('product', $request->query->get('q'), [
    'offset' => 0,
    'limit'  => 20,
    // attribute filters / sort go here, per the bundle's option shape
]);

$ids = array_column($rows, 'id');
$products = $productRepo->findBy(['id' => $ids]);

That three-step shape — query Sphinx for IDs, fetch entities, reorder to match Sphinx relevance — is 90% of what you'll do with this bundle. Everything else is refinement.

Implementation Patterns

Wrap the bundle service; never call it from controllers. The bundle returns arrays with an undocumented, version-sensitive shape. Put a thin ProductSearch service in front of it so the rest of the app depends on your API, not on the bundle's:

class ProductSearch
{
    private $sphinx;
    private $repo;

    public function find($term, $page = 1, $perPage = 20)
    {
        $rows = $this->sphinx->search('product', $term, [
            'offset' => ($page - 1) * $perPage,
            'limit'  => $perPage,
        ]);

        $found = /* total from SHOW META, if exposed; else $rows meta */;

        $entities = $this->repo->findBy(['id' => array_column($rows, 'id')]);
        // key by ID, then walk $rows to preserve Sphinx's relevance order
        return ['hits' => $ordered, 'total' => $found];
    }
}

This is also your extension point for logging slow queries, empty-result fallbacks, and swapping the backend later.

Order preservation matters. findBy() returns rows in whatever order Doctrine chooses. Users notice. Build [$id => $entity] from the Doctrine result and iterate the Sphinx rows to reconstruct ranking.

Pagination needs total_found, not count($rows). Sphinx returns the match count via SHOW META after the query. If the bundle doesn't surface it, run the meta query yourself through the same connection, or accept that your pager is approximate. Also keep in mind max_matches (default 1000) caps how deep you can page at all — set it in sphinx.conf if you need deep pagination, and understand the memory cost.

Filters and sorting are attributes, not text. Sphinx distinguishes full-text fields from attributes. category_id, price, created_at must be declared as attributes in both sphinx.conf and the bundle config, or filter/sort silently returns nothing useful. Typical day-to-day query: text match on title/description, filter on in_stock = 1 and price BETWEEN ..., sort by @weight DESC, price ASC. Get comfortable with the @weight alias — it's the ranking column.

Excerpts come from the original text. Sphinx doesn't store field content; CALL SNIPPETS needs you to pass the source string in. So the pattern is: search → hydrate entities → for the ~20 entities on the page, ask Sphinx for an excerpt of $product->getDescription(). Never build excerpts for the whole result set, only the visible page.

Freshness: pick main+delta or RT. For content that changes, either (a) run indexer --rotate on a cron for a main index plus a small delta index queried alongside it, or (b) use a real-time index and insert/update on postPersist/postUpdate. Main+delta is simpler to operate; RT is simpler to reason about. Don't try to reindex per-request.

Treat it as a search index, not a source of truth. All the display data should still come from the database. That keeps Sphinx's schema changes cheap and means a stale or down index degrades to "no results" rather than "wrong results."

Gotchas and Tips

Symfony 2 only, genuinely. This bundle targets Symfony 2.x and PHP 5.x-era code. It won't autowire cleanly into Symfony 3.4+/4+, and the service definitions use the old XML/container:debug world. If you're on a modern Symfony, plan on either a small compatibility fork (register the services yourself in services.yml, point at the same classes) or replacing it with a ~100-line PDO wrapper over SphinxQL — honestly, the bundle's value is mostly the config-driven index map plus query escaping, and both are cheap to reimplement.

SphinxQL port vs. API port. 9306 is SphinxQL (MySQL protocol); 9312 is the legacy binary API. Point the bundle at the wrong one and you get connection timeouts with no useful error. A plain mysql client is your best debugging tool — if it fails there, it will fail in PHP.

Query escaping and syntax errors are the #1 failure mode. Users type quotes, @, -, (, *. Concatenating those into SphinxQL produces parse errors that surface as generic PDO exceptions. Use whatever escaping the bundle provides; if it doesn't escape, escape yourself (@-prefixed field searches and - negation are meaningful). Also decide up front whether you want MATCH('term') AND semantics or extended-query syntax, and document it for your users.

Tokenizer surprises. Default min_word_len (often 1–3), stopwords, and the stemmer mean "Symfony" and "symfony" match but "running" may not match "run" unless morphology = stem_en is set. Character-set mismatches in sphinx.conf (charset_table) cause silent zero-result queries for accented text. When a query "should" match and doesn't, run it through the mysql client and SHOW META before blaming PHP.

Cache clearing after config changes. Changing index mappings means Symfony's container cache is stale — php app/console cache:clear --env=prod (or rm -rf app/cache/*), then verify with container:debug that the service picked up the new index list.

Connection lifecycle. The bundled PDO connection is long-lived under php-fpm. After a searchd restart you can get "server has gone away." Wrap searches in a small retry, or reconnect on PDOException with a MySQL error code in the 2000s.

Delta reindex ordering. If you run main+delta, the cron must reindex the delta before merging it into main, and --rotate needs write access to the index directory. Log the indexer's exit code; a silently failing cron is the usual cause of "search is two weeks stale."

Testing. In test, either point at a dedicated fixture index built during setup or replace the search service with an in-memory stub via a compiler pass / test container override. Don't let unit tests depend on a running searchd.

Migration note. If you're greenfield and only need the feature set this bundle provides, evaluate Manticore Search (the actively maintained Sphinx fork) with your own thin client. The config-driven mapping is nice, but you'll spend less time on a direct integration than on nursing a Symfony 2 bundle with 40 stars through a modern stack.

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