stinger-soft/entity-search-bundle
A Symfony bundle for adding entity search to your application, with configurable search services and Doctrine entity query support. Lightweight, easy to integrate, and designed for flexible search workflows.
First, the framework reality check. stinger-soft/entity-search-bundle is a Symfony bundle, not a Laravel package.
config/entity-search.php to publish, no Facade. composer require stinger-soft/entity-search-bundle in a Laravel app will at best install silently and do nothing, and at worst fail resolution because its symfony/* and doctrine/* constraints were written for Symfony 4/5-era versions. Run composer why-not stinger-soft/entity-search-bundle before wasting time.Where to look first (in the repo):
DependencyInjection/Configuration.php — the config tree tells you the whole mental model: which search_service backend is aliased, index naming, and facet settings.Model/ — the IndexableEntityInterface (roughly getId(), getDocumentType(), and a way to produce the search document).Service/ — the SearchService / SearchServiceInterface implementations, including the null/dummy backend used for tests.Document/ (or equivalent) — the DocumentBuilder that turns an entity into a flat, typed document.The first use case to understand: an entity (e.g. Product) implements the indexable interface; a builder flattens it into a document with typed fields; SearchService::index() writes it to Elasticsearch via Elastica; SearchService::search() returns IDs or entities plus facet counts. That's the entire loop — everything else is configuration.
In Laravel terms, the closest native equivalent is Laravel Scout + a document transformer. Before adopting anything, prototype that: a Searchable model, a toSearchableArray(), and a meilisearch/elasticsearch driver. If that satisfies you, you never needed this bundle.
Pattern A — Borrow the abstraction, not the code (recommended). Mirror the bundle's shape inside Laravel:
Indexable contract: getSearchId(): string, getDocumentType(): string, toSearchDocument(): array.DocumentBuilder service that assembles the array (typed fields, nested objects, denormalized facet values). This is the piece worth stealing — keep it separate from the model so field extraction is testable and versionable.SearchServiceInterface in a service provider ($this->app->bind(...)) so you can swap Meilisearch ↔ Elasticsearch ↔ a fake in tests, exactly as the bundle swaps backends via its search_service alias.Model::observe() → dispatch IndexEntity/DeleteEntityFromIndex. The bundle has no automatic sync either; you own the lifecycle.Pattern B — Run it as a sidecar. If you genuinely want the bundle's facet/aggregation logic, stand up a small Symfony app that shares (or duplicates) the Doctrine entities, indexes into Elasticsearch, and exposes a search endpoint. Laravel calls it over HTTP. This is the only sane way to use the actual code from a Laravel app.
Pattern C — Symfony-native (for context). Register the bundle in bundles.php, configure the search service and index, implement the interface on entities, register the indexer as a console command, inject SearchServiceInterface into controllers. Facets are configured declaratively and returned alongside hits.
Integration tips that carry across all three:
reindex command; the bundle builds documents but does not manage your Elasticsearch mappings for you.whereIn), never one query per hit.symfony/* ^6|^7). Check before planning around it.deleted/forceDeleted observers are mandatory.DocumentType / index name mistakes fail silently — a typo routes documents to a different index and search returns empty rather than erroring. Log json_encode($document) plus the target index name before every write while debugging.How can I help you explore Laravel packages today?