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

Sonata Elastica Bundle Laravel Package

marmelab/sonata-elastica-bundle

Integrates Elasticsearch with Sonata Admin via Elastica, enabling fast full-text search and filtering for Doctrine entities. Configure indices, index admin-managed records, and use Elasticsearch-backed queries in Sonata lists for scalable search.

View on GitHub
Deep Wiki
Context7

Getting Started

Step 0 — confirm the stack before you composer require anything. This bundle is a Symfony bundle, not a Laravel package. It has no service provider, no facade, and no seam that fits Illuminate\Container. It extends SonataAdminBundle admin classes and replaces Sonata's Doctrine datagrid with an Elastica query. If your admin surface is Filament, Nova, Backpack, or a custom Blade CRUD, there is nothing to install — the value is the ~few-hundred-line pattern it demonstrates, which you reimplement against the official Elasticsearch PHP client. The steps below assume you genuinely have (or are standing up) Symfony + Sonata Admin; otherwise skip to the "Implementation Patterns" section and read it as a design guide.

Install into a Symfony app (Sonata Admin required).

composer require marmelab/sonata-elastica-bundle

Because the repo is archived, expect the constraint to resolve only against an older Symfony/Sonata/Elastica triple. Run composer why-not marmelab/sonata-elastica-bundle first, and read its composer.json for the pinned ruflin/elastica, sonata-project/admin-bundle, and symfony/framework-bundle ranges. Treat those as ceilings, not floors.

Register the bundle (config/bundles.php on Symfony 4+, AppKernel::registerBundles() on 2/3), then add the connection config — this is the first file to read, because it is the entire public API surface:

# config/packages/sonata_elastica.yaml
sonata_elastica:
    connections:
        default:
            servers:
                - { host: "%elasticsearch_host%", port: 9200 }
            # index/type discovery options live here in the bundle's Configuration.php

Where to look first, in order:

  1. DependencyInjection/Configuration.php — what is actually configurable (connection, index prefix, indexable types).
  2. The Admin/ directory — the index/type/document admin classes. This is the code you will fork or copy.
  3. The datagrid wiring (filter and list builder overrides) — the part that makes Sonata's list view read from ES instead of Doctrine. This is the only genuinely novel code in the bundle.
  4. Resources/views/ — Twig templates; you'll override these for anything presentational.

First use case — read-only index exploration. Before touching any application data path, point the bundle at a scratch index and browse it. Log in to /admin, find the Elastica group on the Sonata dashboard, and open an index → type → document. You get mapping inspection, document counts, pagination, and a search box for free. Do this on a dev cluster with a throwaway index. It is the fastest way to confirm the bundle is operational in your environment before you invest in wiring it to real admin screens — and if the dashboard entry doesn't appear, the problem is almost always Sonata admin service registration, not Elasticsearch.

Implementation Patterns

Pattern 1 — Explore and inspect (zero code). The bundle's safest and most immediately useful mode. Register it, point it at the cluster, and use the admin pages as a read-only ES browser: verify a mapping landed correctly after a reindex, eyeball document shape for a type you're debugging, count docs per index after a populate run. Use this even if you never wire it into a production list view.

Pattern 2 — Read-path replacement for one slow Sonata list. The core capability. Sonata's list view normally runs a Doctrine query; the bundle swaps in an Elastica\Query and paginates over hits. You scope this to one admin class at a time:

// Pseudocode shape — verify signatures against the pinned Sonata version.
final class OrderAdmin extends AbstractAdmin
{
    // The bundle supplies the ES-backed datagrid/model-manager plumbing;
    // your admin keeps its Doctrine entity for the edit/detail forms
    // and only the list/filter path is served from ES.
}

Critical constraint: only the read path moves. Create/update/delete still go through Doctrine. The bundle is a UI adapter, not a sync pipeline — it does not index your entities for you. That job belongs to FOSElasticaBundle's fos:elastica:populate commands, Doctrine listeners, or your own outbox. Decide the sync owner before you ship the read path.

Pattern 3 — Faceted, multi-criteria filters for ops screens. The real payoff over Doctrine is filtering on fields that are expensive or unindexed in SQL — full-text over a description, term filters across five dimensions, ranges over dates. Map each Sonata FilterInterface to an ES clause and let ES do the intersection. Keep the filter set small and explicit; each filter you expose is a mapping dependency, and every mapping change is a reindex.

Pattern 4 — Laravel-side reimplementation (the recommended path). If you are a Laravel shop, copy the concept into a repository that returns a LengthAwarePaginator:

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

    public function paginate(array $filters, int $perPage = 25, int $page = 1): LengthAwarePaginator
    {
        // Build ['query' => ['bool' => ['filter' => [...]]]] from $filters.
        // Use from/size only under 10k; switch to search_after beyond that.
        $response = $this->client->search([
            'index' => 'orders',
            'body'  => ['from' => ($page - 1) * $perPage, 'size' => $perPage, 'query' => $this->build($filters)],
        ]);

        return new LengthAwarePaginator(
            $response['hits']['hits'],
            $response['hits']['total']['value'],
            $perPage,
            $page
        );
    }
}

The write side is the hard half: model observers → outbox table → queued indexing job, with idempotent document IDs so replays are safe. This bundle does nothing for you here, and the sync path is where the operational cost actually lives.

Pattern 5 — Sidecar only as a last resort. If you must use Sonata Admin and your system of record is Laravel, run a small separate Symfony app pointed at the same index. Laravel publishes documents; the sidecar reads them. Budget for a second deployable, a second auth/SSO integration, and a duplicated domain model. This is an architecture decision, not an integration task.

Configuration and multi-environment. Always route through an index alias (orders → orders_v3) rather than a concrete name, so a reindex is a swap rather than a config deploy. Keep the connection config environment-driven; never hardcode hosts. If you run multiple clusters (prod read replica vs. dev), give them distinct connection names in sonata_elastica.connections.

Gotchas and Tips

The archive is the headline gotcha. No security patches, no dependency bumps, no compatibility work for Elasticsearch 8+/OpenSearch. If your cluster is 8+, stop: mapping types (which this bundle's index/type/document model is built around) were removed in 7.x, so the core abstraction no longer maps onto the server. The fork-and-own cost is real and recurring — every ES major upgrade lands on you.

Dependency ceilings are hard ceilings. A transitive constraint pinning Symfony or Elastica blocks your whole app's upgrade path. Before any spike, run composer update --dry-run with the bundle in a scratch branch and confirm you can still move PHP and Symfony forward. If you can't, that's your answer.

No sync, no freshness — expect index drift. The bundle reads whatever is in the index. Mutations in your DB do not appear in the admin list until something reindexes. Debug complaints of "I saved it but it's not in the list" by checking the document _id against the index directly, not by reading the bundle source. Tune refresh_interval (e.g. -1 during bulk reindex, 1s after) and remember that refresh=true on writes will cost you throughput.

Mapping mismatches fail silently. A filter that worked against Doctrine can return a wrong but plausible result set against a stale or differently-analyzed ES mapping — text vs keyword is the classic one, where a term filter matches nothing. This is worse than an error. After every mapping change, reindex into a new index behind the alias and smoke-test each exposed filter.

Pagination degrades past 10k. from/size is capped by index.max_result_window. Admin list views on large indices will throw once a user pages deep. Cap the page depth in the admin, or plan a search_after implementation — which the bundle will not give you.

No graceful fallback on ES outage. Historically there is no Doctrine fallback, and arguably there shouldn't be — but you must decide explicitly. If the admin list hard-fails when the cluster is down, ops loses their tools exactly when things are already bad. Consider a cached/empty-state view with a visible banner rather than a 500.

The admin exposes raw documents, including PII. The document browser renders whatever is in the index. Index only fields the back-office may see, or restrict the Elastica admin group by role. This is easy to forget because it looks like an internal dev tool.

Debugging: read the generated query, not the PHP. Override the list builder and dump $query->toArray() — most "wrong results" bugs are a clause you didn't intend, not a bug in the bundle. On the ES side, _explain and the profile API on the offending query will tell you why a document did or didn't match. Sonata's own error pages will not.

Extension points worth knowing before you fork. The admin classes (for columns and templates), the datagrid/filter builders (for query shape), and the Twig templates (for presentation) are the three seams. Everything else is thin glue. If you are copying rather than depending, copy the datagrid wiring and write your own admin surface — that is where the durable value is.

Pin and document your fork. If you adopt, tag a fork, write a one-page upgrade runbook (which versions are pinned, how to reindex, who owns it), and name a maintainer. An archived dependency with no owner is a scheduled outage, not a decision.

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.
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator