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

Symfony Bundle Laravel Package

schranz-search/symfony-bundle

Symfony bundle for schranz-search that adds search engine indexing and querying to Symfony apps via a search abstraction layer spanning Elasticsearch, OpenSearch, Meilisearch, Algolia, and more.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the bundle alongside the search library it wires up:

composer require schranz-search/symfony-bundle

If you do not have Symfony Flex, register the bundle in config/bundles.php:

Schranz\Search\Symfony\Bundle\SchranzSearchBundle::class => ['all' => true],

The first place to look is the generated bundle config reference (bin/console config:dump-reference schranz_search) and the upstream php-cmsig/search / SEAL documentation — this bundle is a thin Symfony wrapper, so nearly all concepts (schemas, engines, adapters, fields) come from the library, not the bundle.

A minimal working config defines one engine, one schema, and one index:

# config/packages/schranz_search.yaml
schranz_search:
    engines:
        default:
            adapter: 'meilisearch'   # or elasticsearch, opensearch, algolia, redis, typesense, solr...
            url: '%env(MEILISEARCH_URL)%'

    schemas:
        posts:
            fields:
                id:    { type: 'keyword' }
                title: { type: 'text' }
                body:  { type: 'text' }

    indexes:
        posts:
            engine: 'default'
            schema: 'posts'

Your first use case is almost always: create the index, push a document, and read it back.

bin/console schranz-search:index:create posts
use Schranz\Search\SEAL\Search\Engine; // or the interface the bundle aliases

final class PostIndexer
{
    public function __construct(private Engine $engine) {}

    public function index(array $post): void
    {
        $this->engine->saveDocument('posts', $post);
    }
}

Autowire the Engine interface (the bundle exposes an alias for it) — do not try to construct adapters yourself, that is exactly what the bundle exists to automate.

Implementation Patterns

Schema-in-config, documents-in-code. Keep field definitions in YAML under schranz_search.schemas; treat PHP classes as the source of truth only for the document payload. A schema change means a new index + reindex, not a runtime mutation.

Dedicated indexer services. Wrap the Engine in small services per aggregate (PostIndexer, ProductIndexer) that translate Doctrine entities into plain document arrays. This keeps controllers free of mapping logic and gives you one place to change when a field is renamed.

final class PostIndexer
{
    public function __construct(private Engine $engine) {}

    public function index(Post $post): void
    {
        $this->engine->saveDocument('posts', [
            'id'    => (string) $post->getId(),
            'title' => $post->getTitle(),
            'body'  => $post->getBody(),
        ]);
    }

    public function remove(Post $post): void
    {
        $this->engine->deleteDocument('posts', (string) $post->getId());
    }
}

Hook these into Doctrine events (postPersist, postUpdate, postRemove) or, better for throughput, dispatch a Symfony Messenger message and index asynchronously.

Bulk indexing for backfills. The single-document API is convenient for real-time updates, but for initial population or reindexing, loop in chunks of 100–500 and use the bulk method exposed by the engine/adapter. This is dramatically faster and avoids per-request overhead.

Search controller as a thin layer. Do not let Engine::search() leak into Twig. Put a SearchService that takes a query string + filters and returns a stable SearchResult DTO. Search request/response shapes differ per adapter, and the bundle passes those through — insulating your app is what makes swapping Meilisearch for Elasticsearch feasible.

final class PostSearch
{
    public function __construct(private Engine $engine) {}

    public function find(string $q): array
    {
        $result = $this->engine->search('posts', $q);
        return iterator_to_array($result); // yields documents
    }
}

Multiple engines. Define more than one entry under engines (e.g. search and analytics) and reference them per index. This is the intended way to separate read-heavy from write-heavy backends without touching service wiring.

Console commands as deployment steps. Treat schranz-search:index:create / index:drop as part of your deploy/release process — either run them in your release script or trigger them from a migration. Never create indexes at request time; you lose atomicity and can race across workers.

Testing with an in-memory or Redis adapter. Configure a lightweight adapter under when@test, point the test suite at it, and reset between tests by dropping/recreating the index. Do not mock the Engine interface — you want to exercise the real schema field mappings.

Gotchas and Tips

  • The READ-ONLY notice. The GitHub repo is a split mirror; issues and PRs go to the php-cmsig/search monorepo. Do not file here; check the upstream project first when debugging.
  • Field types are adapter-specific. A text field works everywhere, but keyword, datetime, and especially geo_point and nested fields are not portable. If you care about swapping engines, stick to the lowest common denominator and verify against each adapter you might deploy.
  • Schema changes require a reindex. There is no in-place migration. The safe pattern is: create posts_v2 with the new schema, backfill, then repoint the alias (or config) and drop posts_v1. Do not edit the field types of a live index in place.
  • The bundle aliases Engine only when it can resolve ambiguity. With multiple engines configured, autowiring Engine $engine may fail — inject by name (e.g. the named alias the bundle exposes) or inject a registry. Read the generated service config with debug:container when in doubt.
  • Async indexing lags. If you use Messenger, expect search to be eventually consistent. Do not immediately ->search() for a document you just saved; tests must either run indexing synchronously or explicitly drain the queue.
  • Missing index = runtime exception. Search calls against a non-existent index fail loudly. Add schranz-search:index:create to onboarding docs and CI setup alongside doctrine:migrations:migrate.
  • Config key changed over versions. Earlier releases used a cmsig_search root key; newer ones use schranz_search. If your config silently does nothing, run config:dump-reference and confirm which key the installed version expects.
  • Debugging: bin/console debug:container --tag=schranz_search.engine (adjust tag to what config:dump-reference / bundle docs show) reveals which services are wired; debug:config schranz_search shows the resolved config after merges. When a query returns nothing, first confirm the document exists via the engine's own HTTP API or dashboard before blaming the abstraction.
  • Extension point to know: adapters live in the search library, not this bundle. If you need a custom adapter or a transform hook, implement/extend there and register it — do not fork the bundle. Likewise, if you need custom schema logic, the library's field classes are what you extend, not anything in this repository.
  • Upgrade the bundle and the search library together. The bundle depends tightly on the library's adapter interfaces; a version skew produces confusing "class not found" errors at cache warmup.
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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums