isswp101/elasticsearch-eloquent
Installation:
composer require isswp101/elasticsearch-eloquent
Ensure your composer.json meets the requirements (PHP 8.0+, Elasticsearch 7.0+).
Define a Model:
Extend BaseElasticsearchModel and set $index (required) and optionally $type (for Elasticsearch 7.x compatibility).
use Isswp101\Persimmon\Models\BaseElasticsearchModel;
class Product extends BaseElasticsearchModel {
protected string $index = 'products';
protected ?string $type = 'product';
}
First Query: Use Eloquent-like syntax to interact with Elasticsearch:
// Create
$product = Product::create(['name' => 'Laptop', 'price' => 999]);
// Query
$results = Product::where('price', '>', 500)->get();
Configuration: Publish the config file (if needed) and customize Elasticsearch client settings:
php artisan vendor:publish --provider="Isswp101\Persimmon\Providers\PersimmonServiceProvider"
CRUD Operations:
create() or save() (mirrors Eloquent).
$product = Product::create(['name' => 'Phone', 'stock' => 10]);
save().
$product->price = 799;
$product->save();
delete() or destroy().
Product::find(1)->delete();
Querying:
Product::where('name', 'like', '%Laptop%')->orderBy('price', 'desc')->get();
$query = Product::query()
->addShould(['name' => 'Phone'])
->addMust(['price' => ['gte' => 500]])
->get();
$results = Product::query()
->addAggregation('avg_price', 'avg', 'price')
->get();
Relationships:
belongsTo, hasMany, etc., but ensure the related models are also Elasticsearch models.
class Order extends BaseElasticsearchModel {
public function products() {
return $this->hasMany(Product::class);
}
}
Bulk Operations:
insert() for bulk creates or update() for bulk updates.
Product::insert([
['name' => 'Tablet', 'price' => 300],
['name' => 'Monitor', 'price' => 200],
]);
Index Management:
php artisan elasticsearch:create-index products
php artisan elasticsearch:update-mapping products
Hybrid Models (SQL + Elasticsearch):
Model and BaseElasticsearchModel to sync data between databases.saved, deleted) to keep Elasticsearch in sync.
class Product extends Model {
use \Isswp101\Persimmon\Traits\ElasticsearchTrait;
protected $elasticsearchEnabled = true;
}
Custom Query Builders:
class ProductQueryBuilder extends \Isswp101\Persimmon\Query\Builder {
public function inStock() {
return $this->where('stock', '>', 0);
}
}
boot():
Product::addGlobalScope(new CustomQueryScope());
Caching:
$products = Cache::remember('featured_products', now()->addHours(1), function() {
return Product::where('featured', true)->get();
});
Testing:
ElasticsearchFake for unit tests (if available) or reset indices between tests.
public function tearDown(): void {
Product::resetIndex();
parent::tearDown();
}
Index/Type Mismatch:
$type is set, the package may fail silently or throw errors.$type or ensure your Elasticsearch version is compatible.Data Type Conflicts:
integer, keyword, text).config/elasticsearch.php or use update-mapping artisan command.
'mappings' => [
'properties' => [
'price' => ['type' => 'float'],
'name' => ['type' => 'text', 'fields' => ['raw' => ['type' => 'keyword']]],
],
],
Case Sensitivity:
where() clauses are case-sensitive by default for keyword fields.lower() or upper() in queries or normalize data before indexing.Relationship Loading:
with() explicitly:
$order = Order::with('products')->find(1);
Bulk Operation Limits:
$products->chunk(500, function ($chunk) {
Product::insert($chunk->toArray());
});
Connection Issues:
createPersistence().try {
$product->save();
} catch (\Elasticsearch\Common\Exceptions\ElasticsearchException $e) {
Log::error('Elasticsearch error: ' . $e->getMessage());
}
Query Logging:
config/elasticsearch.php:
'logging' => true,
Kibana Validation:
Model Events:
boot() to log or validate data before Elasticsearch operations:
protected static function boot() {
parent::boot();
static::saving(function ($model) {
if (empty($model->name)) {
throw new \Exception('Product name cannot be empty.');
}
});
}
Index Existence:
if (!Product::indexExists()) {
Product::createIndex();
}
Custom Persistence:
createPersistence() to use a custom Elasticsearch client (e.g., with retry logic or middleware).
public function createPersistence(): PersistenceContract {
$client = Elasticsearch\ClientBuilder::create()
->setHosts(['http://es:9200'])
->setRetryOnConflict(3)
->build();
return new Persistence($client);
}
Query Scopes:
class Product extends BaseElasticsearchModel {
public function scopeOnSale($query) {
return $query->where('discount', '>', 0);
}
}
Usage:
Product::onSale()->get();
Dynamic Indexing:
protected string $index = config('app.env') === 'production' ? 'products_prod' : 'products_dev';
Webhook Triggers:
Product::observe(ProductObserver::class);
class ProductObserver {
public function saved(Product $product) {
if ($
How can I help you explore Laravel packages today?