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

Elastica Extra Bundle Laravel Package

jbafford/elastica-extra-bundle

jbafford/elastica-extra-bundle is a Symfony bundle that layers extra services, helpers and integration glue on top of the Elastica Elasticsearch client, making it easier to register clients and wire search features into a Symfony application's service container.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm the mismatch before writing code. This is a Symfony bundle: it registers through config/bundles.php, loads services via a DependencyInjection/Extension class, and may run compiler passes. Laravel has none of those lifecycle hooks. composer require jbafford/elastica-extra-bundle will not register anything automatically — there is no extra.laravel.providers. Treat the install as reference source only until you've read it, and decide between (a) vendoring specific helpers into app/Services/Search/ or (b) rebuilding the wiring directly. Do not try to boot the bundle inside Laravel.

Where to look first, in this order:

  1. Composer metadata — composer show jbafford/elastica-extra-bundle to see the pinned ruflin/elastica constraint and Symfony component versions. This tells you immediately whether the Elastica generation supports your target Elasticsearch cluster.
  2. DependencyInjection/ — the extension's load() method and any compiler passes. This is the bundle's actual value: a list of service definitions and how they're assembled. Everything else is plumbing you can ignore.
  3. Resources/config/*.xml|yml — the raw service definitions. These are your migration checklist.
  4. The helper classes themselves (repositories, paginators, persisters, index builders). Read each one and ask: "is this 30 lines I could own?"

First working use case (about 30 minutes). Bind Elastica\Client as a singleton in a Laravel service provider, create an index, insert one document, and query it back. This confirms the client connects to your cluster before you invest in porting helper logic:

// app/Providers/ElasticServiceProvider.php
public function register(): void
{
    $this->app->singleton(Client::class, function ($app) {
        return new Client([
            'connections' => [
                [
                    'host' => config('elastic.host'),
                    'port' => config('elastic.port'),
                ],
            ],
        ]);
    });
}
// config/elastic.php — translate the bundle's semantic config tree here
return [
    'host' => env('ELASTIC_HOST', 'localhost'),
    'port' => (int) env('ELASTIC_PORT', 9200),
];
$index = app(Client::class)->getIndex('blog');
$index->create(['settings' => [...], 'mappings' => [...]], true);
$index->addDocument(new \Elastica\Document('1', ['title' => 'Hello']));
$index->refresh();
$results = $index->search('hello')->getResults();

If that spike fails on a version mismatch, you've learned the most important thing in half an hour.

Implementation Patterns

Bind the client once, inject it everywhere. A singleton binding plus constructor injection into repositories is the whole pattern. Never let Elastica\Client or Elastica\Index types cross into controllers — wrap them:

class PostRepository
{
    public function __construct(private Client $client) {}

    public function index(Post $post): void
    {
        $this->client->getIndex('posts')->addDocument(
            new \Elastica\Document($post->id, $post->toSearchableArray())
        );
    }

    public function search(string $q, int $page = 1, int $perPage = 20): array
    {
        $query = new \Elastica\Query(new \Elastica\Query\MultiMatch($q, [
            'fields' => ['title^2', 'body'],
        ]));
        $query->setFrom(($page - 1) * $perPage)->setSize($perPage);
        return $this->client->getIndex('posts')->search($query)->getHits();
    }
}

Model integration on Eloquent events, not on the bundle's persister. The bundle almost certainly ships a persister/hydrator pattern tied to Symfony's event dispatcher. In Laravel, the idiomatic equivalent is observer-driven: Post::saved → call PostRepository::index(), Post::deleted → deleteDocument(). Queue the write (ShouldQueue listener) so an Elasticsearch outage does not roll back a database transaction.

Translate the semantic config, key by key. Walk the bundle's Configuration::getConfigTreeBuilder() and write a flat config/elastic.php that mirrors it. Anything that reads $container->getParameter(...) becomes config('elastic....'). Anything that resolves environment-specific index names (the bundle's "index per environment" convenience is typically its most useful contribution) becomes:

'index_prefix' => env('APP_ENV') === 'production' ? '' : env('APP_ENV') . '_',

That one line is often the actual reason to read this package.

Reproduce compiler passes with tagged bindings. If the bundle tags services (e.g. elastica.index tags collected into a registry), Laravel's equivalent is $this->app->tag([PostIndexer::class], 'search.indexers') and resolving with $this->app->tagged('search.indexers'). Don't port the compiler pass mechanism — port the outcome.

Reindexing as a first-class command. Give yourself an artisan command early: create a new index with a versioned name, bulk-index from the database with Elastica\Bulk, then swap an alias. Elastica\Client supports bulk operations natively; wrap that in a command and schedule it.

Test against a real cluster. Elastica has no in-memory fake worth relying on. Use a containerized Elasticsearch at your exact target version (Testcontainers or a docker-compose service) and run a small contract suite: index a doc, query it, assert the hit shape. This is also your version-compatibility canary.

Gotchas and Tips

  • Elastica types are a trap on ES 7+. Much of the Elastica-era API is organized around index types, which Elasticsearch removed. If the bundle's helpers take (index, type) tuples, they will not translate. Check this in the source before anything else.
  • No facade, no auto-discovery, no artisan vendor:publish. Everything is manual. Register the provider in config/app.php (or bootstrap/providers.php on Laravel 11) and expect to hand-write all config.
  • Composer resolver conflicts are the most likely first failure. This package pulls Symfony DI components; many Laravel installs already have Symfony components pinned by framework constraints. If composer require explodes with an unrelated-looking constraint error, that's the cause — don't chase phantom bugs.
  • config:cache will freeze the client config. Any dynamically computed host (e.g. from a service discovery call) must be resolved in the binding closure, not at config-load time, or cache clearing becomes part of your deploy.
  • Connection pooling and retries are not surfaced. Elastica's defaults are dated. Explicitly set timeouts and a retry strategy in the client config array, and instrument failures yourself — the library emits no telemetry. Wrap the repository boundary with timing/counter middleware.
  • Debugging search misses: call $index->refresh() in non-production before asserting in tests, and dump ->getQuery()->toArray() — the Elastica query builder silently drops some nested clauses if the DSL shape doesn't match the client version.
  • Extension points are your exit ramp. Keep exactly one public interface (SearchRepository) in front of everything. That single decision means you can swap Elastica for the official elasticsearch/elasticsearch client — a realistic near-term move given zero upstream dependents and a bus factor of one — without touching application code.
  • Pin the version explicitly ("jbafford/elastica-extra-bundle": "x.y.*") and record the decision in an ADR. There is no upstream to notify you of a breaking change; your pin is the safety net.
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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
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