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

Elasticquent Laravel Package

ximil/elasticquent

Elasticquent maps Laravel Eloquent models to Elasticsearch, letting you index records, run fuzzy and geo searches, and sync data through simple traits and methods so your app gets fast, flexible full-text search without hand-writing queries.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish config

composer require ximil/elasticquent
php artisan vendor:publish --provider="Elasticquent\ElasticquentServiceProvider"

This drops config/elasticquent.php with default_index, default_type, prefix, hosts, logging, logPath, logLevel. Set hosts and default_index first — everything else in the package resolves against those two values.

Wire up a model

use Elasticquent\ElasticquentTrait;

class Post extends Model
{
    use ElasticquentTrait;

    protected $fillable = ['title', 'body', 'published_at'];

    // Optional: per-model override of the config defaults
    protected $indexName = 'posts';
    protected $typeName  = 'post';

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

First use case: create the index and search

Post::createIndex();          // create the ES index with your mapping
Post::addAllToIndex();        // push every row into ES

$results = Post::search('laravel elasticsearch');

foreach ($results as $post) {
    echo $post->title;
}
echo $results->getTotal();

Where to look first in the repo: composer.json (which elasticsearch/elasticsearch client version the fork pins — this dictates your ES server version), the ElasticquentTrait source (it is the entire public API), and ElasticquentResultCollection (the object search() returns).


Implementation Patterns

1. Keep ES in sync with model events, not manual calls

The trait does not auto-index on save. The standard pattern is an observer:

class PostObserver
{
    public function saved(Post $post)   { $post->addToIndex(); }
    public function deleted(Post $post) { $post->removeFromIndex(); }
}
// AppServiceProvider::boot()
Post::observe(PostObserver::class);

If you need write latency out of the request cycle, dispatch a queued job instead of calling addToIndex() inline.

2. Bulk reindex as an artisan command

Post::reindex() deletes and recreates the index and re-adds everything — fine for small tables, dangerous for large ones. For real workloads, chunk it yourself:

Post::chunkById(500, function ($posts) {
    foreach ($posts as $post) { $post->addToIndex(); }
});

For serious volume, skip the trait for writes and use the underlying elasticsearch-php bulk endpoint directly in a scheduled/queued command.

3. Per-tenant or per-environment index naming

Override getIndexName() on the model so you get isolation without touching config:

public function getIndexName()
{
    return config('elasticquent.prefix') . 'posts_' . tenant()->id;
}

Then createIndex() / deleteIndex() in your tenant provisioning/teardown jobs.

4. Querying: search() for convenience, searchByQuery() / complexSearch() for control

// Simple query_string
$results = Post::search('title:laravel');

// Explicit query DSL + aggregations
$results = Post::searchByQuery([
    'bool' => [
        'must'   => [['match' => ['body' => 'elasticsearch']]],
        'filter' => [['term' => ['status' => 'published']]],
    ],
], $aggregations = [
    'by_tag' => ['terms' => ['field' => 'tags', 'size' => 20]],
]);

$results->getAggregation('by_tag');

complexSearch(array $params) takes the raw request body (index, type, body with from/size/sort/_source), which is the escape hatch when the helpers don't cover a case.

5. Treat search results as models, but re-fetch when you need relations

Results are hydrated Eloquent instances from _source, so $post->title works and $post->relation triggers a lazy load. That means N+1 on listing pages. Two safe patterns:

  • Search for IDs, then Post::with('author')->whereIn('id', $ids)->get() and reorder in PHP.
  • Or accept the hydration and eager-load explicitly by collecting IDs first.

6. Wrap it in a repository

interface PostSearch { public function find(array $criteria): Collection; }

Because the trait is static-method-heavy and hard to mock, having one seam around it means you can swap to elasticsearch-php directly later without touching controllers.


Gotchas and Tips

  • No automatic indexing. addToIndex(), addAllToIndex(), removeFromIndex() are all explicit. Every "why is my search stale" bug starts here.
  • The document body comes from toArray(). $hidden and $visible therefore control what gets indexed — a hidden body column silently disappears from ES. $casts and date serialization also determine whether your mapping (date, numeric types) will accept the document.
  • Mapping changes need a rebuild. Elasticsearch can't change an existing field's type. putMapping() only adds fields; changing an analyzer or type means create a new index, repopulate, then flip an alias.
  • createIndex() fails if the index already exists. Guard with an existence check or catch the exception before calling it in deploy scripts.
  • Refreshed search is not immediate. ES refresh interval means a document indexed in a test can be missing from the next query. Pass refresh in your params (or refresh=true) for test suites and for read-after-write flows.
  • Type deprecation. The package's _type-based API (default_type, getTypeName()) maps onto ES 1.x–6.x semantics. On ES 7 it works only in compatibility mode; on ES 8 types are gone. Confirm the pinned elasticsearch/elasticsearch version in composer.json matches your cluster before adopting the package.
  • hosts format drifts between client majors. The string form ('localhost:9200') used by older configs isn't accepted by newer clients that expect ['host' => ..., 'port' => ...]. Check the generated config against the installed client version.
  • Hydration and $guarded. Models come back via newInstance-style construction from _source; if you rely on $guarded for security, verify fields aren't being dropped. If anything looks missing, either extend $fillable or handle the raw hits yourself ($results->getHits()).
  • Debug with the built-in logger. elasticquent.logging = true plus a writable logPath gives you the full request/response, which is far faster than guessing at mapping errors.
  • Fork risk. This is a low-activity fork (0 dependents, 0 stars) of the original Elasticquent. Pin the exact version in composer.json, and avoid depending on undocumented internals. The public surface you're actually leaning on is small — trait methods plus the result collection — so keep a fallback plan to call the elasticsearch-php client directly.
  • Extension points. Override getIndexName(), getTypeName(), getMappingProperties(), and getIndexSettings() on the model rather than editing the trait; those four cover almost every customization you'll need.
  • Bulk writes and memory. addAllToIndex() loads the whole table. Never run it against a large model outside a chunked command or a queue job.
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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel