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

Elasticquent5 Laravel Package

aloko/elasticquent5

Elasticquent5 brings Elasticsearch to your Laravel Eloquent models with a simple trait, letting you index, search, and query documents without leaving the familiar Eloquent API.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and wire it up

composer require aloko/elasticquent5

This is a Laravel-5-era fork of elasticquent/elasticquent, so it pulls in the elasticsearch/elasticsearch 5.x PHP client and expects an Elasticsearch 5 cluster. Check composer.lock after install — the client version it resolves is the single biggest constraint on your project.

Register the provider. On Laravel 5.5+ package discovery handles Aloko\Elasticquent\ElasticquentServiceProvider automatically; on 5.0–5.4 add it to config/app.php:

'providers' => [
    // ...
    Aloko\Elasticquent\ElasticquentServiceProvider::class,
],

Publish the config so you can point it at your cluster:

php artisan vendor:publish --provider="Aloko\Elasticquent\ElasticquentServiceProvider"

config/elasticquent.php holds the default index name and the client options (hosts, logging, log path, log level). Put the host in .env rather than hard-coding it, and make sure the log path is writable if you turn logging on.

First use case: index a model and search it

use Elasticquent\ElasticquentTrait;

class Book extends Eloquent
{
    use ElasticquentTrait;

    protected $fillable = ['title', 'author', 'summary'];

    protected $mappingProperties = [
        'title'   => ['type' => 'text', 'analyzer' => 'standard'],
        'author'  => ['type' => 'text', 'analyzer' => 'standard'],
        'summary' => ['type' => 'text', 'analyzer' => 'english'],
    ];
}

Then, from php artisan tinker:

Book::createIndex();          // create index + apply mapping
Book::addAllToIndex();        // bulk-load existing rows
$books = Book::search('moby dick');
$books->getTotal();           // ES hit count, not count($books)

Look first at: the trait (to see exactly which methods your fork exposes and how it builds request bodies), config/elasticquent.php, and ElasticquentResultCollection — that result wrapper is where getTotal(), getTook() and aggregations live.

Implementation Patterns

Treat the index as a derived store, never as the source of truth. The trait does not hook into Eloquent events. Wire sync yourself in the model's boot():

protected static function boot()
{
    parent::boot();

    static::saved(fn ($model) => $model->addToIndex());
    static::deleted(fn ($model) => $model->removeFromIndex());
}

For anything with real write volume, push that to a queued job (dispatch(new IndexBook($book->id))) so a slow cluster never fails a user's save. Guard the listener with a config flag (config('elasticquent.sync', true)) so seeders, imports and test runs don't hammer the cluster.

Reindex through a dedicated artisan command, chunked.

addAllToIndex() and reindex() walk the whole table. Write your own command that chunks and builds a fresh index, then flips an alias — that gives you a zero-downtime mapping change instead of an outage:

Book::where('id', '>', $lastId)->orderBy('id')->chunk(500, function ($books) {
    foreach ($books as $book) { $book->addToIndex(); }
});

Search deliberately — two entry points. search('free text') is a convenience wrapper (usually a multi_match across your mapping); searchByQuery(array $query) gives you the raw ES query DSL plus aggregations, _source filtering, limit/offset and sort as positional arguments:

$results = Book::searchByQuery(
    ['bool' => ['must' => [['match' => ['title' => $term]]],
                'filter' => [['term' => ['status' => 'published']]]]],
    ['by_author' => ['terms' => ['field' => 'author']]],   // aggregations
    ['title', 'author'],                                   // _source
    20, 0,                                                 // limit, offset
    ['_score' => 'desc']
);

$results->getTotal();
$results->getAggregations();

Results are hydrated from _source into real Eloquent models, so accessors, casts and relation loading all work on them — but attributes come from the index, not the database.

Isolate the package behind a small interface. Because this is a low-traffic fork on a pinned client, put search behind BookSearchRepository with just the methods your app calls (search, index, remove, reindex). Replacing it with Laravel Scout or the official ES client later becomes a one-class change instead of a hunt through controllers.

Model configuration tips. Override getIndexName(), getTypeName(), getMapping() and getIndexSettings() explicitly rather than relying on the table-name default — especially if you have multiple models that could land in the same index. Use one index per environment (books_development, books_production) driven from config.

Pagination. There is no built-in paginator. Build a LengthAwarePaginator from $results and $results->getTotal() yourself; remember ES from + size is capped by index.max_result_window (10 000 by default), so deep pages need search_after.

Gotchas and Tips

  • Elasticsearch 5 only. ES 6 deprecated mapping types and ES 7 removed them; this client's index/type request shape will not work against a modern cluster. Do not let your infra team upgrade ES underneath this package without migrating the code.
  • type => 'string' is gone in ES 5. Half the Elasticquent tutorials online use it. Use text (analyzed) and keyword (exact/aggregatable).
  • Mappings are immutable once a field exists. You cannot change a field from text to keyword in place — you must create a new index, repopulate, and swap an alias. Plan this from day one.
  • No automatic refresh. addToIndex() then immediate search() can return nothing for up to the refresh interval (~1 s). In tests, index with refresh=true (via the raw client) or sleep(1) before asserting.
  • No model-event syncing out of the box. Deletes done via query builder (Book::where(...)->delete()) bypass your deleted hook entirely; schedule a reconciliation job if that's a real code path.
  • addAllToIndex() / reindex() are memory hogs and reindex() is destructive (drops the index before rebuilding). Never run it against production without an alias swap or a snapshot.
  • Static vs instance calls. The documented usage is Book::search(...). Those trait methods are instance methods in most variants; older PHP tolerates the static call (with a deprecation notice), PHP 8 throws. If you're pinned to a modern runtime, use (new Book)->search(...) or add your own static wrapper.
  • Hydration ignores DB scopes. Soft-deleted or since-updated rows still come back as models from _source. If the DB is authoritative, whereIn('id', $results->pluck('id')) in the database and re-order by the ES hit order.
  • toArray() leaks into the index. Hidden attributes, appended accessors, Carbon date formatting and relation arrays all end up in _source unless the trait filters them — check what gets stored and override the indexing payload if needed.
  • Silent failures. The client throws on connection errors but document-level indexing problems can be easy to miss; enable 'logging' => true in config and tail the log while you're bringing this up. Also confirm storage/logs (or wherever logPath points) is writable.
  • default_index in config vs getIndexName() are two different things and a common source of "why are my docs in the wrong index" confusion. Set $indexName on the model and stop guessing.
  • Don't mix ->where() with search results. Filtering the returned ElasticquentResultCollection happens in PHP, after the cluster already did the work — it's not a query.
  • Maintenance risk is real: 21 stars, no dependents, a fork of an abandoned lineage. Pin the exact version in composer.json, keep it isolated behind your repository class, and keep Laravel Scout + an ES driver or the official elasticsearch/elasticsearch 7/8 client on the roadmap for when this blocks a framework or cluster upgrade.
  • Extension points worth knowing: getIndexName(), getTypeName(), getMapping(), getIndexSettings() and the indexing payload method are all overridable on the model — they're the cleanest places to inject per-tenant index names or custom analyzers without touching vendor code.
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.
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk