alamirault/elasticsearch-form-bundle
Installation:
composer require alamirault/elasticsearch-form-bundle
Register the bundle in config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 3):
Alamirault\ElasticsearchBundle\AlamiraultElasticsearchFormBundle::class => ['all' => true],
Configure Elasticsearch Client (config/packages/alamirault_elasticsearch_form.yaml):
alamirault_elasticsearch_form:
clients:
default:
connections:
- { host: "localhost", port: 9200 }
logger: "@logger"
indexes:
users:
mapping: "%kernel.project_dir%/config/elasticsearch/mapping/users.yml"
Create a Form Type:
// src/Form/ExampleFilterType.php
use Alamirault\ElasticsearchBundle\Form\ElasticFilterTypeInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class ExampleFilterType extends AbstractType implements ElasticFilterTypeInterface
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('uuid', TextType::class, [
'elastic_filter_condition' => function (?string $value) {
return new \Elastica\Query\Match('uuid', $value);
},
]);
}
}
First Use Case:
Inject ElasticsearchMaker in a controller and handle form submission:
use Alamirault\ElasticsearchBundle\Maker\ElasticsearchMakerInterface;
public function index(Request $request, ElasticsearchMakerInterface $elasticsearchMaker, FormFactoryInterface $formFactory)
{
$form = $formFactory->create(ExampleFilterType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$results = $elasticsearchMaker->manageForm($form, $request, 'users');
// Render results
}
}
Field-Specific Elasticsearch Conditions:
Attach query builders to form fields via elastic_filter_condition:
$builder->add('name', TextType::class, [
'elastic_filter_condition' => function (?string $value) {
return $value ? new \Elastica\Query\QueryString($value) : null;
},
]);
new \Elastica\Query\Term('field', $value)new \Elastica\Query\Wildcard('field', $value . '*')new \Elastica\Query\Range('age', ['gte' => 18])new \Elastica\Query\BoolQuery() for AND/OR/NOT.Dynamic Query Composition:
Use ElasticsearchMaker::manageForm() to merge all field conditions into a single query:
$query = $elasticsearchMaker->manageForm($form, $request, 'index_name');
$results = $elasticsearchMaker->makeSearch('index_name', $query);
Pagination and Sorting:
Leverage Symfony’s Paginator with Elasticsearch’s from/size:
$query->setFrom($page * $limit)->setSize($limit);
$sort = new \Elastica\Sort('timestamp', \Elastica\Sort::DESC);
$query->addSort($sort);
Integration with Serialization:
Use Symfony’s Serializer to normalize/denormalize documents:
$normalized = $serializer->normalize($entity);
$doc = new \Elastica\Document($id, $normalized);
$index->addDocument($doc);
Elasticsearch Version Mismatch:
ruflin/elastica v6.x, which may not support Elasticsearch 7+ features (e.g., _doc type)._doc for ES <7.2.0:
$type = new \Elastica\Type($index, '_doc'); // Only for ES <7.2.0
Null Handling in Filters:
null) may break queries. Always check:
'elastic_filter_condition' => function (?string $value) {
return $value ? new \Elastica\Query\Match('field', $value) : null;
},
Mapping Configuration:
dynamic: true, which may cause performance issues.config/elasticsearch/mapping/*.yml and reference them in the bundle config.Circular References in Serialization:
User->Posts->User) will fail.@Ignore annotations or customize the serializer context:
$normalized = $serializer->normalize($entity, null, [
'ignored_attributes' => ['posts'],
]);
Proxy Configuration:
proxy option is set in the bundle config:
connections:
- { host: "proxy.example.com", port: 8080, proxy: "http://real-es:9200" }
Log Raw Queries:
Enable debug logging for the logger.api channel to inspect queries:
services:
logger.api:
class: Monolog\Logger
arguments: ["elasticsearch"]
calls:
- [pushHandler, ["@monolog.handler"]]
Test Queries Manually:
Use ElasticsearchMaker::makeSearch() directly to test queries:
$query = new \Elastica\Query\MatchAll();
$results = $elasticsearchMaker->makeSearch('users', $query);
Handle Deprecation Warnings: Elasticsearch 7+ emits deprecation warnings for legacy APIs. Suppress them in the client config:
$client->getConnection()->setDeprecationMode(\Elastica\Connection::DEPRECATION_MODE_SILENT);
Custom Query Builders:
Extend the ElasticFilterTypeInterface to add reusable conditions:
class CustomFilterType extends AbstractType implements ElasticFilterTypeInterface
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('date_range', RangeType::class, [
'elastic_filter_condition' => [$this, 'buildDateRangeQuery'],
]);
}
public function buildDateRangeQuery(?array $range)
{
if (!$range) return null;
return new \Elastica\Query\Range('created_at', $range);
}
}
Post-Processing Results: Use Symfony’s event system to modify results before rendering:
$eventDispatcher->addListener(
'elasticsearch.results',
function (ResultsEvent $event) {
$event->setResults($event->getResults()->map(...));
}
);
Bulk Operations:
Batch document updates using IndexMaker:
$index = $elasticsearchMaker->getIndex('users', IndexMaker::WRITE);
$bulk = new \Elastica\Bulk();
foreach ($users as $user) {
$bulk->addDocument($index, $user->getId(), $user->getData());
}
$bulk->send();
How can I help you explore Laravel packages today?