Installation
Run composer require sleimanx2/plastic to add the package to your Laravel project.
Publish the config file with php artisan vendor:publish --provider="Sleimanx2\Plastic\PlasticServiceProvider".
Add the Elasticsearch connection to your .env:
ELASTICSEARCH_CONNECTION=default
ELASTICSEARCH_HOSTS=http://localhost:9200
Basic Setup
Define an Elasticsearch model by extending Sleimanx2\Plastic\ElasticModel:
use Sleimanx2\Plastic\ElasticModel;
class Product extends ElasticModel
{
protected $index = 'products';
protected $type = '_doc';
}
First Query Use the fluent query builder to search:
$results = Product::query()
->where('name', 'like', 'laptop%')
->paginate(10);
Indexing a Model Sync a model with Elasticsearch:
$product = Product::create(['name' => 'MacBook Pro', 'price' => 1999]);
$product->syncToElastic(); // Manually sync
// OR
Product::observe(ElasticObserver::class); // Auto-sync on create/update
Mapping Models
Define mappings in a mappings() method:
class Product extends ElasticModel
{
public function mappings()
{
return [
'properties' => [
'name' => ['type' => 'text', 'analyzer' => 'english'],
'price' => ['type' => 'float'],
'tags' => ['type' => 'keyword'],
],
];
}
}
Run php artisan plastic:mappings to apply mappings to Elasticsearch.
Querying with Aggregations Use aggregations for analytics:
$results = Product::query()
->terms('category', 'categories')
->paginate();
Hybrid Search (SQL + Elasticsearch) Combine Laravel Eloquent with Elasticsearch queries:
$products = Product::query()
->where('price', '>', 1000)
->elasticWhere('name', 'like', 'pro%')
->get();
Bulk Operations
Use bulkIndex() for efficient indexing:
Product::bulkIndex([
['name' => 'Product A', 'price' => 100],
['name' => 'Product B', 'price' => 200],
]);
Real-Time Updates Observe model events for auto-sync:
class Product extends ElasticModel
{
protected static function booted()
{
static::observe(ElasticObserver::class);
}
}
$cached = Cache::remember('products_search', now()->addHours(1), function () {
return Product::query()->where('name', 'like', 'laptop%')->get();
});
PlasticTestCase for testing:
use Sleimanx2\Plastic\Testing\PlasticTestCase;
class ProductTest extends PlasticTestCase
{
public function testSearch()
{
$this->assertCount(1, Product::query()->where('name', 'MacBook')->get());
}
}
Index/Type Confusion
_doc as the default type. Ensure $type = '_doc' in your model.type field in mappings (use properties instead).Mapping Conflicts
php artisan plastic:mappings --force
->ignoreMappings() to skip mapping checks during queries.Connection Issues
ELASTICSEARCH_HOSTS in .env includes the correct protocol (http:// or https://).Plastic::connection('custom')->query(...) for multi-connection setups.Performance with Large Datasets
->select(['field1', 'field2']) to limit returned fields.Product::bulkIndex(array_chunk($products, 1000));
Observer Conflicts
ElasticObserver is the last observer in the chain to avoid duplicate syncs.Enable Logging
Add to config/plastic.php:
'log' => [
'enabled' => true,
'channel' => 'single',
],
Check logs in storage/logs/laravel.log.
Query Inspection
Use ->toElasticQuery() to see the raw Elasticsearch query:
$query = Product::query()->where('name', 'like', 'pro%')->toElasticQuery();
dd($query);
Reindexing Reindex all records with:
php artisan plastic:reindex App\Models\Product
Custom Analyzers
Define analyzers in mappings():
public function mappings()
{
return [
'settings' => [
'analysis' => [
'analyzer' => [
'custom_analyzer' => [
'type' => 'custom',
'tokenizer' => 'standard',
'filter' => ['lowercase', 'asciifolding'],
],
],
],
],
'properties' => [
'name' => ['type' => 'text', 'analyzer' => 'custom_analyzer'],
],
];
}
Custom Query Builders
Extend Sleimanx2\Plastic\Query\Builder for domain-specific queries:
class ProductQueryBuilder extends Builder
{
public function inStock()
{
return $this->where('stock', '>', 0);
}
}
Bind it to your model:
class Product extends ElasticModel
{
protected $queryBuilder = ProductQueryBuilder::class;
}
Hooks for Pre/Post Sync
Override syncingToElastic() and syncedToElastic() in your model:
protected function syncingToElastic(array $data)
{
$data['processed_at'] = now();
return $data;
}
Multi-Tenancy Use dynamic indices based on tenant:
class Product extends ElasticModel
{
protected $index = 'products_' . auth()->id();
}
How can I help you explore Laravel packages today?