Installation:
composer require becklyn/search-bundle
Add to config/bundles.php:
Becklyn\SearchBundle\BecklynSearchBundle::class => ['all' => true],
Configure Elasticsearch:
Add to config/packages/becklyn_search.yaml:
becklyn_search:
client:
host: 'http://localhost:9200'
index_prefix: 'app_'
Annotate an Entity:
use Becklyn\SearchBundle\Entity\SearchableEntityInterface;
use Becklyn\SearchBundle\Mapping as Search;
/**
* @Search\Item()
*/
class Product implements SearchableEntityInterface
{
// ...
}
First Indexing:
php bin/console becklyn:search:index Product
Search for products in a controller:
use Becklyn\SearchBundle\Search\SearchInterface;
class ProductController
{
public function search(SearchInterface $search)
{
$results = $search->search('Product', 'query');
return $this->render('product/search.html.twig', ['results' => $results]);
}
}
Indexing:
becklyn:search:index EntityName).prePersist, preUpdate):
use Becklyn\SearchBundle\Event\IndexEntityEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class IndexSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
IndexEntityEvent::INDEX => 'indexEntity',
];
}
public function indexEntity(IndexEntityEvent $event)
{
$event->getIndexer()->index($event->getEntity());
}
}
Searching:
$results = $search->search('Product', 'query');
$query = [
'query' => [
'bool' => [
'must' => [
['match' => ['name' => 'query']],
['range' => ['price' => ['gte' => 10]]],
],
],
],
];
$results = $search->search('Product', $query);
Pagination:
$results = $search->search('Product', 'query', 1, 10); // page, per_page
SearchType for search form integration:
use Becklyn\SearchBundle\Form\Type\SearchType;
$builder->add('search', SearchType::class, [
'entity' => 'Product',
'property' => 'name',
]);
#[ApiResource]
class ProductSearchController
{
#[GET('/search', name: 'search_products')]
public function search(SearchInterface $search, string $query): JsonResponse
{
return new JsonResponse($search->search('Product', $query));
}
}
Index Naming Conflicts:
index names in @Search\Item() must be unique. Defaults to app_entity_name.index_prefix in config to avoid collisions.Loader Services:
loader, ensure the service is properly tagged (becklyn.search.loader).var/log/dev.log for missing service errors.Localization:
LocalizedSearchableEntityInterface, ensure getLanguage() returns a valid LanguageInterface object.becklyn/interfaces package for consistency.Mapping Updates:
php bin/console becklyn:search:reindex Product
Elasticsearch Connection:
ConnectionException. Handle gracefully:
try {
$results = $search->search('Product', 'query');
} catch (\Exception $e) {
// Fallback to database or notify admin
}
APP_DEBUG=true) for detailed logs in var/log/dev.log.php bin/console becklyn:search:status
Custom Mappers:
Becklyn\SearchBundle\Mapper\MapperInterface to handle custom field mappings:
use Becklyn\SearchBundle\Mapper\MapperInterface;
class CustomMapper implements MapperInterface
{
public function map($entity): array
{
return [
'custom_field' => $entity->getCustomValue(),
];
}
}
services:
App\Mapper\CustomMapper:
tags:
- { name: 'becklyn.search.mapper', entity: 'Product' }
Event Listeners:
IndexEntityEvent or SearchEvent for custom logic:
use Becklyn\SearchBundle\Event\SearchEvent;
class CustomSearchListener
{
public function onSearch(SearchEvent $event)
{
$event->getQuery()->addParam('custom_param', 'value');
}
}
services:
App\Listener\CustomSearchListener:
tags:
- { name: 'kernel.event_listener', event: 'becklyn.search.query', method: 'onSearch' }
Async Indexing:
use Becklyn\SearchBundle\Message\IndexEntityMessage;
$bus->dispatch(new IndexEntityMessage($entity));
config/packages/messenger.yaml.How can I help you explore Laravel packages today?