chaplean/elasticsearch-bundle
Installation:
composer require chaplean/elasticsearch-bundle
Ensure your project uses Symfony 3.4+ (or 4.x/5.x).
Register Bundle:
Add to config/bundles.php (Symfony 4+) or app/AppKernel.php (Symfony 3):
Chaplean\Bundle\ElasticsearchBundle\ChapleanElasticsearchBundle::class => ['all' => true],
Configure Indexes:
Define Elasticsearch indexes in config/packages/chaplean_elasticsearch.yaml:
chaplean_elasticsearch:
indexes:
products: 'products_index' # Maps 'products' service key to 'products_index' in Elasticsearch
articles: 'articles_index'
First Use Case:
Inject the ElasticsearchClient service (auto-registered) into a controller/service:
use Chaplean\Bundle\ElasticsearchBundle\Service\ElasticsearchClient;
class ProductSearchController extends AbstractController
{
public function search(ElasticsearchClient $client)
{
$index = $client->getIndex('products'); // Resolves to 'products_index'
$results = $index->search(['query' => ['match_all' => new \stdClass()]]);
return $this->json($results);
}
}
Index Management:
createIndex() method to define mappings dynamically:
$index = $client->getIndex('products');
$index->create([
'mappings' => [
'properties' => [
'name' => ['type' => 'text'],
'price' => ['type' => 'float']
]
]
]);
bulk() method for efficient indexing:
$index->bulk([
['index' => ['_id' => 1]],
['name' => 'Laptop', 'price' => 999.99],
['index' => ['_id' => 2]],
['name' => 'Phone', 'price' => 699.99]
]);
Query Patterns:
$results = $index->search([
'query' => [
'multi_match' => [
'query' => 'wireless headphones',
'fields' => ['name^3', 'description']
]
]
]);
$results = $index->search([
'aggs' => [
'price_ranges' => [
'range' => [
'field' => 'price',
'ranges' => [
['to' => 500],
['from' => 500, 'to' => 1000],
['from' => 1000]
]
]
]
]
]);
Integration with Doctrine:
ElasticsearchEntityManager to sync Doctrine entities:
$entityManager = $client->getEntityManager(Product::class);
$entityManager->persist($product);
$entityManager->flush(); // Syncs to Elasticsearch
@Chaplean\Bundle\ElasticsearchBundle\Annotation\Indexed on entities.Event-Driven Sync:
# config/packages/chaplean_elasticsearch.yaml
chaplean_elasticsearch:
sync:
enabled: true
events: ['postPersist', 'postUpdate', 'postRemove']
Index Naming Collisions:
indexes keys in config are unique and match your Elasticsearch cluster’s naming conventions.ecommerce_products_v1).Connection Issues:
elasticsearch/elasticsearch client under the hood.config/packages/chaplean_elasticsearch.yaml for client configuration:
chaplean_elasticsearch:
client:
hosts: ['http://localhost:9200']
# Add auth if needed:
# auth: ['username', 'password']
hosts: ['%env(ES_HOST)%']
Doctrine Sync Quirks:
@Indexed, ensure the entity has a getId() method (Elasticsearch requires _id).chaplean_elasticsearch:
sync:
enabled: false
Mapping Conflicts:
type from text to integer).index->putMapping() carefully.Enable Logging:
Add to config/packages/monolog.yaml:
handlers:
elasticsearch:
type: stream
path: "%kernel.logs_dir%/elasticsearch.log"
level: debug
Then configure the bundle to log queries:
chaplean_elasticsearch:
debug: true
Raw Client Access: Access the underlying client for advanced queries:
$rawClient = $client->getClient();
$params = $rawClient->indices()->get(['index' => 'products_index']);
Testing:
Use the ElasticsearchTestCase trait for PHPUnit:
use Chaplean\Bundle\ElasticsearchBundle\Test\ElasticsearchTestCase;
class ProductSearchTest extends ElasticsearchTestCase
{
public function testSearch()
{
$this->assertSearchReturns(1, ['query' => ['match_all' => new \stdClass()]]);
}
}
Custom Index Classes:
Extend Chaplean\Bundle\ElasticsearchBundle\Service\Index to add domain-specific methods:
class ProductIndex extends Index
{
public function searchByCategory(string $category)
{
return $this->search([
'query' => [
'term' => ['category.keyword' => $category]
]
]);
}
}
Register as a service:
services:
app.product_index:
class: App\Service\ProductIndex
arguments: ['@chaplean_elasticsearch.client', 'products']
tags: ['chaplean_elasticsearch.index']
Query Builders: Create reusable query DSLs:
class ProductQueryBuilder
{
public function buildSearch(array $filters): array
{
$query = ['bool' => ['must' => []]];
if (isset($filters['price_max'])) {
$query['bool']['must'][] = ['range' => ['price' => ['lte' => $filters['price_max']]]];
}
return ['query' => $query];
}
}
Async Processing: Use Symfony Messenger to queue Elasticsearch operations:
$message = new IndexProductMessage($product);
$this->messageBus->dispatch($message);
Configure the handler to use the ElasticsearchClient.
---
How can I help you explore Laravel packages today?