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 Laravel Package

maximenko/elastica

Laravel integration for Elasticsearch built on the Elastica client, with configuration and a convenient service-based API for indexing documents, running queries, and managing indices from PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Before writing a line of code, understand what you're holding: this is a low-adoption package with zero stars and zero dependents. Read the vendor source in vendor/maximenko/elastica before you build against it — you will not find Stack Overflow answers or GitHub issue threads when it misbehaves.

composer require maximenko/elastica:*@dev

If it registers a provider, publish the config and inspect it:

php artisan vendor:publish --provider="Maximenko\Elastica\ElasticaServiceProvider"

If no provider is auto-discovered, register it manually in config/app.php (or bootstrap/providers.php on Laravel 11) and bind a singleton yourself:

$this->app->singleton(\Elastica\Client::class, function ($app) {
    $cfg = $app['config']['elastica'];
    return new \Elastica\Client([
        'hosts' => $cfg['hosts'],
    ]);
});

Where to look first:

  1. composer show maximenko/elastica — check the version, PHP constraint, and require block. If the constraint is loose or missing, pin it.
  2. The package's composer.json and the Elastica version it vendors or depends on. Elastica majors track Elasticsearch majors; a stale fork likely targets an old ES line.
  3. config/elastica.php (or the published config) — hosts, transport options, index prefix.

First use case — verify connectivity before anything else:

use Elastica\Client;

$client = app(Client::class);
$health = $client->getStatus(); // or $client->request('_cluster/health')->getData();
dump($health);

Do this against a staging cluster that mirrors production security settings (TLS, auth). Most failures with older Elastica forks against ES 8 surface at the transport layer, not in query results — you want to find that on day one, not during a reindex.

Your first real operation should be: create an index, index one document, retrieve it, delete it. That exercises the whole Index → Type → Document path and confirms the fork's API surface matches the upstream Elastica docs you'll be reading.

Implementation Patterns

Wrap the client behind your own interface

Do not let Elastica\* types leak into controllers, models, or jobs. Define your own contract and put the Elastica adapter behind it:

interface SearchClient
{
    public function index(string $index, string $id, array $doc): void;
    public function bulk(string $index, array $docs): BulkResult;
    public function search(string $index, array $query, array $opts = []): array;
    public function delete(string $index, string $id): void;
}

This costs one class and buys you the ability to swap in elasticsearch/elasticsearch or ruflin/elastica later without touching call sites. Given the package's provenance, that option is worth more than usual.

The Index → Document workflow

Elastica's object model is Client → Index → Type → Document. In modern ES (7+), types are gone, but Elastica still surfaces them — use _doc as the type name and treat it as a formality:

$index = $client->getIndex('products');
$index->create(['settings' => [...], 'mappings' => [...]], true);

$doc = new \Elastica\Document($id, $attributes);
$index->addDocument($doc);
$index->refresh(); // only when you need read-after-write

refresh() on every write is a common trap — it forces a shard refresh and tanks bulk throughput. Call it only in tests or when a user flow genuinely requires immediate visibility.

Bulk indexing

Bulk is where Elastica actually earns its place — hand-rolling the NDJSON body and _bulk endpoint is the main thing the client saves you. Pattern for a queued job:

$index = $client->getIndex('products');
$docs = array_map(fn ($row) => new \Elastica\Document($row['id'], $row), $chunk);

$response = $index->addDocuments($docs);
$index->refresh();

if ($response->hasError()) {
    // partial failures are silent unless you check
    foreach ($response->getData()['items'] ?? [] as $item) {
        if (($item['index']['status'] ?? 200) >= 300) {
            logger()->warning('bulk item failed', $item);
        }
    }
}

Batch 500–1000 docs per call. Use a queued job (ShouldQueue) so a slow cluster doesn't block the request cycle. Instrument the failure count — a bulk call that "succeeds" with silent item failures is the classic way to end up with index gaps.

Search and aggregations

$query = new \Elastica\Query();
$query->setQuery(new \Elastica\Query\MultiMatch($term, ['name', 'description']));
$query->setSize(20);
$query->setSource(['id', 'name', 'price']); // don't return the whole doc

$aggs = new \Elastica\Aggregation\Terms('by_brand');
$aggs->setField('brand')->setSize(10);
$query->addAggregation($aggs);

$result = $client->getIndex('products')->search($query);

Always set _source filtering on list endpoints. Returning full documents is the fastest way to exhaust PHP memory on a size: 100 query with large fields.

Configuration

Keep credentials out of the config file — pull from .env:

// config/elastica.php
return [
    'hosts' => [
        [
            'host' => env('ELASTIC_HOST', 'localhost'),
            'port' => env('ELASTIC_PORT', 9200),
            'scheme' => env('ELASTIC_SCHEME', 'http'),
        ],
    ],
];

If the fork's config doesn't support basic auth or TLS options, you'll need to pass them in the hosts array directly to the Client constructor — check the source for what keys it forwards.

Testing

Bind a fake or an in-memory double for SearchClient in your test suite; don't hit a real cluster in unit tests. For integration tests, use a throwaway index per test and delete it in tearDown(). If you point tests at a real cluster, tag them @group integration and keep them out of CI's fast path.

Gotchas and Tips

Verify the ES version matrix before you build anything real. This is the single highest-risk item. Run $client->getVersion() and compare against your server's GET /. If the client reports a version far behind your cluster, expect breakage on ES 8 defaults (TLS + auth required, mapping types removed, _doc API changes). Do not assume compatibility from the package description.

Silent bulk failures are the norm, not the exception. addDocuments() returns a response object; a 200 at the top level does not mean every document indexed. Always inspect items and log non-2xx statuses. Add a metric for bulk failure rate from day one.

refresh() is a performance lever, not a correctness crutch. If you find yourself calling it on every write, you probably want a small delay in the UX, or to read from the primary for that user's next request. Reserve forced refreshes for tests.

Types are vestigial. If examples reference $index->getType('product')->addDocument($doc), that's ES 6-era API. On ES 7+ everything is _doc; the type still appears in Elastica's API but is a compatibility shim. Don't design around it.

No retry/backoff unless you add it. Elastica (and this fork) will not retry a failed request for you. For anything user-facing, wrap search calls in a try/catch with a fallback path (return cached results, degrade to a DB query, show an empty state). A slow cluster with no circuit breaker becomes a slow application.

Instrument what the client won't tell you: query p50/p95 latency, error rate, bulk failure count, index size growth. Assume the client reports nothing useful and build the visibility yourself.

Connection reuse matters under Octane. If you're on Laravel Octane (Swoole/RoadRunner), a long-lived Elastica\Client singleton holds persistent HTTP connections. That's fine and desirable — but verify the client resets connection state between requests and that timeouts are configured, or you'll see stale-connection failures after cluster restarts. On standard FPM, none of this matters.

Extension points are thin. Don't expect event hooks, query logging, or a debug toolbar integration. The practical extension point is the Client's underlying Guzzle transport — you can inject a custom handler or middleware at construction if you need request logging or instrumentation. Check the fork's constructor signature; it may differ from upstream Elastica's.

Debugging a query builder mismatch: when a query "returns nothing" but you're sure the data is there, log the actual JSON the client sends ($query->toArray() before search()), then paste it into Kibana Dev Tools against the same index. That isolates whether the bug is in your DSL or in the client's serialization — which, in a fork, is a real possibility.

If you hit a genuine client bug, you own it. No upstream issue tracker will help. Prefer: (a) work around it in your adapter layer, (b) extract the fix as a patch onto upstream ruflin/elastica if it applies there, (c) vendor the package locally. Never patch inside vendor/ without a fork you control — the next composer install wipes it.

Revisit the decision at your next ES upgrade. Treat the cluster-upgrade ticket as the forced re-evaluation point: at that moment, swapping the adapter for a maintained client is a one-file change if you followed the interface pattern above, and a multi-week migration if you didn't.

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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource