maximenko/elastica-bundle
A Symfony bundle that integrates the Elastica Elasticsearch client, offering configuration, service wiring, and convenient indexing and search tools. It simplifies adding Elasticsearch-powered search to Symfony applications.
Reality check first. This package is a Symfony2 bundle, not a Laravel package. It ships a Bundle class, a DependencyInjection extension, and compiler passes — none of which Laravel's container can register. Do not attempt composer require maximenko/elastica-bundle in a Laravel app hoping for a ServiceProvider; there isn't one, and the config merge path doesn't exist. Additionally, the artifact is a zero-star, zero-dependent fork of FOSElasticaBundle with mismatched name/repo provenance — treat it as unvetted code and do not vendor it.
What you actually adopt day-to-day. The reusable piece is the underlying transport: ruflin/elastica (framework-agnostic PHP client) or, preferably, the official elasticsearch/elasticsearch client. If you want the "bundle" experience in Laravel, that's Elastica + a hand-written service provider.
Minimal steps:
composer require elasticsearch/elasticsearch (or ruflin/elastica if you specifically want its query builder ergonomics).app/Providers/SearchServiceProvider.php), reading connection config from config/search.php / .env (ELASTIC_HOST, ELASTIC_PORT, ELASTIC_SCHEME).SearchRepository (or SearchService) that wraps the client and exposes index(), bulkIndex(), search(), delete().config/search.php — this is the equivalent of the bundle's YAML index definitions.Product) via bulkIndex() from an Artisan command, then query it from a controller. Confirm you can round-trip a document against your actual cluster version before writing any sync logic.Where to look first. If you have a Symfony codebase or are migrating one, look at upstream FOSElasticaBundle's docs for index config, persistence listeners, and the fos:elastica:populate command — those concepts map cleanly onto what you'll rebuild. If you're staying in Laravel, skip the bundle docs entirely and read the official Elasticsearch PHP client docs plus Laravel Scout + an ES driver (e.g. tpetry/laravel-scout-elasticsearch) as the lower-maintenance comparator.
Verify the triple before you build anything: PHP constraint vs. your runtime, Elasticsearch server major version vs. client version, and whether you even need the sync layer. If it's a small read-mostly feature, a direct client call in one service class beats a sync framework.
Service provider + singleton client. Bind the client once, resolve it via constructor injection, never new it in controllers.
// config/search.php
return [
'hosts' => [env('ELASTIC_HOST', 'localhost:9200')],
'index_prefix' => env('ELASTIC_INDEX_PREFIX', 'app_'),
'models' => [
\App\Models\Product::class => [
'index' => 'products',
'mapping' => [/* properties */],
],
],
];
Mapping-as-config. Keep index definitions in one config file, not scattered in model code. This is the one genuinely useful idea you inherit from the bundle's design: declare indexes/types once, reference them everywhere. Treat mappings as versioned artifacts — a mapping change is a deploy event, not a code tweak.
Eloquent observers for sync — but queued. The bundle's analogous feature is Doctrine event listeners that update the index on persist/update/delete. In Laravel the equivalent is Model::observe() or model events dispatching a queued job:
class ProductObserver {
public function saved(Product $p) { IndexProduct::dispatch($p)->onQueue('search'); }
public function deleted(Product $p) { RemoveProduct::dispatch($p->id)->onQueue('search'); }
}
Keep writes off the request cycle. Synchronous indexing on save is what makes the bundle pattern not scale under write-heavy workloads — don't reproduce that flaw.
Bulk population via Artisan. Mirror the bundle's populate command with php artisan search:populate products --chunk=500, using bulk() with chunks. Make it idempotent and re-runnable; you will run it constantly during development and after every mapping change.
Repository abstraction over the client. One SearchRepository per index (or a generic base) that owns query construction, so controllers and jobs never touch the raw client API. This gives you a single seam to swap Elastica for the official client later without touching call sites.
Zero-downtime reindex via aliases. Read from an alias (products), write to a versioned concrete index (products_v2), then atomically swap the alias. This is not provided by any wrapper — it's the operational pattern you own regardless of library. Bake it in from the start; retrofitting it after launch is painful.
Consistency model decision (explicit). Choose queued-sync-with-retries + a periodic reconciliation command, or accept drift. Write it down. The bundle's listener approach silently assumes sync success; you should not.
ServiceProvider, no auto config merge, no bootstrap/app.php registration path. There is no integration seam; the "evaluation" collapses to the client library. Confirm your stack before any spike._type), old query DSL. ES 7.x removed types, 8.x changed the transport/protocol further. A stale fork very likely emits malformed requests against a modern cluster, often silently (empty results rather than errors). Pin and verify.maximenko/elastica-bundle) doesn't match the repo it points to (FOSElasticaBundle), zero stars, zero dependents. That's a personal fork. You would be the first production user and you'd own the fork — PHP bumps, protocol updates, CVE patching, all internal.search:reconcile command that compares counts/updated_at and repopulates diffs, and alert on drift.curl the cluster directly with the same JSON body. Most "wrapper bugs" are actually mapping/version mismatches — bypassing the abstraction isolates it in seconds.How can I help you explore Laravel packages today?