Installation:
composer require friendsofsymfony/elastica-bundle
Enable the bundle in config/bundles.php:
return [
// ...
FriendsOfSymfony\ElasticaBundle\FOSElasticaBundle::class => ['all' => true],
];
Configuration:
Add Elasticsearch client config in config/packages/fos_elastica.yaml:
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
app:
client: default
settings:
number_of_shards: 3
types:
product:
properties:
name: ~
price: ~
First Use Case:
Create a Product entity and annotate it for indexing:
use FriendsOfSymfony\ElasticaBundle\Annotation as Elastica;
#[Elastica\Document(indexName: 'app', type: 'product')]
class Product
{
#[Elastica\Property(name: 'name', type: 'string')]
private string $name;
#[Elastica\Property(name: 'price', type: 'float')]
private float $price;
}
Indexing: Run the command to generate and populate the index:
php bin/console fos:elastica:populate
Entity Indexing:
@Document, @Property) or YAML/XML config for mapping.config/packages/fos_elastica.yaml):
fos_elastica:
indexes:
app:
types:
product:
properties:
name: { type: string, analyzer: standard }
price: { type: float, scale: 2 }
Dynamic Indexing:
fos_elastica:
indexes:
app:
types:
dynamic_product: ~
Serializer Integration:
fos_elastica.yaml:
fos_elastica:
serializer:
name: fos_elastica.serializer.default
JmsSerializer or Symfony’s Serializer for custom serialization.Doctrine Listeners:
postPersist, postUpdate, postRemove:
fos_elastica:
listeners:
~: true # Enable all listeners
Querying:
use FriendsOfSymfony\ElasticaBundle\Repository;
class ProductRepository extends Repository
{
public function findByName($name)
{
return $this->createQueryBuilder()
->addMust('match', ['name' => $name])
->getQuery()
->getResults();
}
}
$products = $this->get('fos_elastica.finder.product')->find($query);
Bulk Operations:
populate command for initial data load:
php bin/console fos:elastica:populate
reindex:
php bin/console fos:elastica:reindex
Symfony Forms:
ElasticaType for search forms:
use FriendsOfSymfony\ElasticaBundle\Form\Type\ElasticaType;
$builder->add('search', ElasticaType::class, [
'finder' => $this->get('fos_elastica.finder.product'),
'property' => 'name',
'multiple' => true,
]);
API Platform:
ElasticaBundle with ApiPlatform for GraphQL/REST search:
# config/packages/api_platform.yaml
api_platform:
formats:
jsonld:
mime_types: ['application/ld+json']
patch_formats:
json: true
jsonld: true
Cron Jobs:
cron:
0 3 * * * php /path/to/your/project/bin/console fos:elastica:reindex
Testing:
ElasticaTestCase for unit tests:
use FriendsOfSymfony\ElasticaBundle\Tests\ElasticaTestCase;
class ProductTest extends ElasticaTestCase
{
public function testIndexing()
{
$this->assertTrue($this->get('fos_elastica.index.product')->exists('test_id'));
}
}
Index Naming Conflicts:
indexName in @Document matches the config. Mismatches cause silent failures.php bin/console fos:elastica:validate
Dynamic Mapping Overrides:
fos_elastica:
indexes:
app:
settings:
mapping:
dynamic: strict
Serializer Conflicts:
JmsSerializer, ensure no circular references exist in entities.@MaxDepth to annotations or configure serializer groups.Listener Order:
@Order to prioritize:
#[Elastica\Listener(order: 100)]
class CustomListener
{
// ...
}
Bulk Indexing Limits:
fos_elastica.yaml:
fos_elastica:
batch_size: 500
Case Sensitivity:
keyword type for exact matches:
properties:
sku: { type: keyword }
Enable Logging:
Add to config/packages/monolog.yaml:
monolog:
handlers:
elastica:
type: stream
path: "%kernel.logs_dir%/elastica.log"
level: debug
Query Debugging:
Use getQuery()->getElasticaQuery() to inspect raw queries:
$query = $this->get('fos_elastica.finder.product')->createQueryBuilder()
->addMust('match', ['name' => 'test'])
->getQuery();
dump($query->getElasticaQuery()->getQuery());
Index Validation: Run:
php bin/console fos:elastica:validate
Fix errors before populating.
Custom Processors:
Elastica\Processor\ProcessorInterface for pre/post-indexing logic:
use FriendsOfSymfony\ElasticaBundle\Processor\ProcessorInterface;
class CustomProcessor implements ProcessorInterface
{
public function process($document, $indexName, $typeName)
{
$document['custom_field'] = 'value';
return $document;
}
}
services.yaml:
services:
App\Processor\CustomProcessor:
tags:
- { name: fos_elastica.processor, index: app, type: product }
Event Subscribers:
fos_elastica.index events:
use FriendsOfSymfony\ElasticaBundle\Event\IndexEvent;
class CustomSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
IndexEvent::INDEX => 'onIndex',
];
}
public function onIndex(IndexEvent $event)
{
$event->getDocument()['custom'] = true;
}
}
Custom Finder:
Finder for project-specific logic:
use FriendsOfSymfony\ElasticaBundle\Finder\FinderInterface;
class CustomFinder extends Finder implements FinderInterface
{
public function findOneByCustom($criteria)
{
// Custom logic
}
}
services.yaml:
services:
fos_elastica.finder.product:
class: App\Finder\CustomFinder
How can I help you explore Laravel packages today?