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

Elasticsearch Laravel Package

addons/elasticsearch

A Laravel/PHP addon that integrates Elasticsearch into your application, making it easy to index Eloquent models and run fast, flexible full-text searches through a simple, configurable bridge between Laravel and Elasticsearch.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the helper alongside Scout and the official Elasticsearch PHP client:

composer require fly-studio/laravel-scout-elasticsearch
php artisan vendor:publish --provider="FlyStudio\Elasticsearch\ElasticsearchServiceProvider"

Look first at:

  1. config/elasticsearch.php (or the published config) — hosts, index prefix, and whether logging is on.
  2. The package's src/ directory — specifically the Paginator, Collection, and Logger classes, since the public API is thin and you will mostly be using those three directly.
  3. Your config/scout.php — confirm whether this package is used as a Scout engine or as a standalone query helper layered on top of the official client. In most installs it is the latter.

Minimal first use case — run a search and hand the result to a paginator:

use FlyStudio\Elasticsearch\ElasticsearchManager;

$hits = app(ElasticsearchManager::class)->search('posts', [
    'body' => [
        'query' => ['multi_match' => [
            'query'  => $request->q,
            'fields' => ['title^3', 'body'],
        ]],
    ],
]);

return $hits->paginate(15); // LengthAwarePaginator backed by ES hits

If you are driving this through Scout models instead, confirm the driver name in config/scout.php and run php artisan scout:import "App\Models\Post" before your first query. The collection helper is the natural place to map _source documents into Eloquent models or API resources.

Implementation Patterns

Repository wrapper. The helpers are most useful when hidden behind a repository, so controllers never touch ES query DSL:

abstract class ElasticsearchRepository
{
    public function __construct(protected ElasticsearchManager $es) {}

    abstract protected function index(): string;
    abstract protected function query(array $filters): array;

    public function paginate(array $filters, int $perPage = 15)
    {
        return $this->es
            ->search($this->index(), ['body' => $this->query($filters)])
            ->paginate($perPage);
    }

    public function collect(array $filters): \Illuminate\Support\Collection
    {
        return $this->es
            ->search($this->index(), ['body' => $this->query($filters)])
            ->collect();
    }
}

Paginator + API Resource. Because the paginator implements Laravel's pagination contract, you can hand it straight to Resource::collection():

return PostResource::collection(
    $repo->paginate($request->only('q', 'category'))
);

Keep from/size conversion inside the repository — do not let controllers compute offsets, or you will end up with ES max_result_window errors leaking into your HTTP layer.

Collection + hydration. The collection helper returns a plain Illuminate\Support\Collection, which is the right tool for aggregations and non-model results (facets, suggesters, analytics dashboards). Use it for anything where you do not need Eloquent models:

$facets = $repo->collect($filters)
    ->pluck('_source.category')
    ->countBy();

Logger as a dev-time query inspector. Enable the package's logger in local/staging, dump the DSL to the elasticsearch log channel, and disable it in production. This is the fastest way to diff the query you think you built against the query that was actually sent:

// config/elasticsearch.php
'logging' => [
    'enabled' => env('ES_LOG_QUERIES', false),
    'channel' => env('ES_LOG_CHANNEL', 'stack'),
],

Pair it with php artisan tail while writing DSL — the logger is the package's real day-to-day value.

Sync discipline. If Scout is the write path (Model::searchable()), keep ES as a derived store: index on model events, reindex with scout:import, and never treat ES as authoritative. Add a queued job or nightly scout:import for drift repair.

Gotchas and Tips

  • Depth pagination is capped. Elasticsearch's index.max_result_window defaults to 10,000. The paginator will happily construct a from beyond that and throw a search_phase_execution_exception. Guard by returning the paginator's last valid page or switching to search_after for deep scrolling.
  • total shape differs by ES version. ES 7+ returns hits.total as an object ({ value, relation }) and only counts up to 10,000 unless you set track_total_hits. If page counts look wrong, that is usually why — set 'track_total_hits' => true on searches where exact totals matter.
  • Sorting on text fields fails silently or loudly. You must sort on .keyword subfields (title.keyword) or you will get a fielddata is disabled error. The logger makes this obvious; the exception message alone often does not.
  • Mapping changes are not automatic. Editing a model's toSearchArray() does not alter the ES mapping. Delete and recreate the index (scout:flush + scout:import) during development; in production, use aliases and reindex, then swap.
  • Hydration loses _id unless you handle it. _source typically does not contain the primary key. If the collection helper maps to models, map _id back to the model key, or store id in the document explicitly.
  • Config cache bites. After changing hosts or index prefixes in production, run php artisan config:clear (or config:cache in the deploy script). A stale cached config is the single most common cause of "it works locally" ES failures.
  • Logger overhead is real. Query logging serializes the full DSL on every request. Keep it behind an env flag and a separate channel so it does not swamp your main log.
  • No typed public API yet. At 46 stars this package is small and the helpers mirror the underlying client closely. Pin the version in composer.json and add an integration test around your repository that hits a real ES container (or testcontainers), because the package itself has no meaningful test harness for downstream users.
  • Extension point. If you need custom behavior (e.g., adding track_total_hits globally, or a custom logger format), extend the manager/logger class and rebind it in a service provider rather than editing vendor code — the package has no extend() hook of its own.
  • Not a Scout driver replacement. Confirm this in your Scout config: the package helps you format results and log queries, but index management and model synchronization still come from Scout plus your ES client. Plan accordingly.
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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo