Installation:
composer require friendsofsymfony/elastica-bundle
Enable the bundle in config/bundles.php:
return [
// ...
FriendsOfSymfony\ElasticaBundle\FriendsOfSymfonyElasticaBundle::class => ['all' => true],
];
Configure Elasticsearch Connection (config/packages/fos_elastica.yaml):
fos_elastica:
clients:
default: { host: 'localhost', port: 9200 }
indexes:
app:
client: default
settings:
number_of_shards: 3
analysis:
analyzer:
custom_analyzer:
type: custom
tokenizer: standard
filter: [lowercase, asciifolding]
First Use Case:
Create a simple indexer for a Doctrine entity (e.g., Product):
# config/packages/fos_elastica.yaml
fos_elastica:
indexes:
app:
types:
product:
properties:
name: ~
price: ~
persistence:
driver: orm
model: App\Entity\Product
provider: ~
listener: ~
Run the indexer:
php bin/console fos:elastica:populate
Automatic Indexing with Doctrine: Use listeners to sync Elasticsearch with Doctrine changes:
fos_elastica:
indexes:
app:
types:
product:
persistence:
listener: true
onFlush, onFlushClear, postUpdate, postPersist, postRemove.Custom Indexing Logic: Override the default serializer or transformer for complex objects:
// src/Elastica/ProductTransformer.php
namespace App\Elastica;
use App\Entity\Product;
use Elastica\Transformer\AbstractTransformer;
class ProductTransformer extends AbstractTransformer
{
public function transform(Product $product)
{
return [
'name' => $product->getName(),
'price' => $product->getPrice(),
'tags' => $product->getTags()->toArray(),
];
}
}
Configure in fos_elastica.yaml:
fos_elastica:
indexes:
app:
types:
product:
persistence:
transformer: App\Elastica\ProductTransformer
Querying Elasticsearch: Use the repository pattern to query:
// src/Repository/ProductElasticaRepository.php
namespace App\Repository;
use FriendsOfSymfony\ElasticaBundle\Repository;
class ProductElasticaRepository extends Repository
{
public function findByName($name)
{
return $this->createQueryBuilder()
->addKeywords('name', $name)
->getResults();
}
}
Inject and use in services/controllers:
$products = $this->productElasticaRepository->findByName('laptop');
Hybrid Search (SQL + Elasticsearch): Use Elasticsearch for full-text/fuzzy search and Doctrine for exact matches:
// Fallback to Doctrine if Elasticsearch returns no results
$elasticResults = $this->productElasticaRepository->findByName('laptp');
if (empty($elasticResults)) {
$doctrineResults = $this->productRepository->findBy(['name' => 'laptp']);
}
Bulk Indexing: Optimize performance with bulk operations:
php bin/console fos:elastica:populate --batch-size=100
Dynamic vs. Static Mapping:
Partial Updates: Update specific fields without reindexing entire documents:
$product = $this->productRepository->find($id);
$product->setPrice(999.99);
$this->elasticaManager->getIndex('app')->getType('product')->update($product->getId(), [
'price' => 999.99,
]);
Mapping Conflicts:
?ignore=404 in updates or ensure consistent transformers.$this->elasticaManager->getIndex('app')->getType('product')->update($id, $data, ['ignore' => 404]);
Listener Overhead:
fos:elastica:populate directly.Serializer Mismatches:
fos_elastica:
indexes:
app:
types:
product:
persistence:
serializer: fos_elastica.serializer.symfony
groups: ['product']
Index Refresh Delay:
refresh for immediate visibility:
$this->elasticaManager->getIndex('app')->refresh();
Check Index Status:
php bin/console fos:elastica:status
Log Elasticsearch Queries:
Enable debug mode in config/packages/fos_elastica.yaml:
fos_elastica:
clients:
default:
logging: true
Test Locally with Docker:
Use docker.elastic.co/elasticsearch/elasticsearch:8.12.0 for local development:
# docker-compose.yml
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- "9200:9200"
Custom Indexers:
Implement FriendsOfSymfony\ElasticaBundle\Indexer\IndexerInterface for non-Doctrine data sources (e.g., CSV, APIs).
Event Subscribers: Extend indexing logic via events:
// src/EventSubscriber/ProductIndexSubscriber.php
namespace App\EventSubscriber;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\LifecycleEventArgs;
class ProductIndexSubscriber implements EventSubscriber
{
public function getSubscribedEvents()
{
return ['postPersist', 'postUpdate', 'postRemove'];
}
public function postPersist(LifecycleEventArgs $args)
{
$product = $args->getObject();
// Custom indexing logic
}
}
Dynamic Index Management:
Use the IndexManager to create/drop indexes programmatically:
$indexManager = $this->elasticaManager->getIndexManager();
$indexManager->createIndex('app');
$indexManager->deleteIndex('app');
Aggregations: Leverage Elasticsearch aggregations for analytics:
$queryBuilder = $this->createQueryBuilder();
$queryBuilder->addAggregation(
'price_stats',
'stats',
'price'
);
$results = $queryBuilder->getResults();
How can I help you explore Laravel packages today?