massive/search-bundle
Laravel bundle that adds a flexible, driver-based search layer to your app. Define searchable models and fields, run queries across multiple sources, and return consistent results with pagination and filtering—designed to be easy to integrate and extend.
Installation:
composer require massive/search-bundle
Add to config/bundles.php (Symfony) or configure via Laravel's service provider equivalent (if using a bridge like symfony/bridge).
Configure Backend:
Edit config/packages/massive_search.yaml (Symfony) or equivalent Laravel config:
massive_search:
clients:
default:
type: elasticsearch
host: 'http://localhost:9200'
index_name: 'app_%kernel.environment%'
adapters:
default: ~
First Use Case: Annotate an entity to enable search:
use Massive\SearchBundle\Annotation as Search;
/**
* @Search\Index(name="product", type="product")
*/
class Product {}
Reindex via CLI:
php artisan massive:search:reindex Product
Basic Query:
$searchManager = $container->get('massive_search.search_manager');
$results = $searchManager->search('product', [
'query' => 'title:php',
'page' => 1,
'limit' => 10
]);
Entity Indexing:
@Search\Index annotations or YAML/XML mappings for automatic indexing.php artisan massive:search:reindex Product --locale=en
Query Construction:
$query = $searchManager->createQueryBuilder('product')
->where('title = :title', ['title' => 'Laravel'])
->andWhere('price > 10')
->getQuery();
$query = [
'bool' => [
'must' => [
['match' => ['title' => 'Laravel']],
['range' => ['price' => ['gt' => 10]]]
]
]
];
Pagination & Sorting:
$results = $searchManager->search('product', [
'query' => $query,
'sort' => ['created_at' => 'desc'],
'page' => 2,
'limit' => 20
]);
Event-Driven Extensions:
Subscribe to SearchEvent to modify queries or results:
$eventDispatcher->addListener(SearchEvent::PRE_SEARCH, function (SearchEvent $event) {
$event->getQuery()->addBoost('title', 2.0);
});
Laravel-Specific:
Use MassiveSearchServiceProvider to bridge Symfony components:
$this->app->bind('massive_search.search_manager', function ($app) {
return new \Massive\SearchBundle\Search\SearchManager(
$app['massive_search.client.default'],
$app['massive_search.adapter.default']
);
});
Custom Field Mappings:
Override default mappings via massive_search.yaml:
massive_search:
mappings:
Product:
fields:
title:
type: text
analyzer: custom_analyzer
Multi-Backend Queries:
$multiSearcher = $container->get('massive_search.multi_searcher');
$results = $multiSearcher->search([
'product' => ['query' => 'title:php'],
'article' => ['query' => 'content:laravel']
]);
PHP/Doctrine Version Mismatch:
composer require symfony/*:^6.4|^7.0 php:^8.2
Serialization Loops:
IndexMetadata. Use:
// In a custom event subscriber
$event->getQuery()->setOption('serialize_metadata', false);
Locale-Specific Indexing:
php artisan massive:search:deindex Product --locale=fr
Zend Lucene Quirks:
optimize command:
php artisan massive:search:optimize
Query Validation:
Enable debug mode in massive_search.yaml:
massive_search:
debug: true
Logs raw queries to var/log/massive_search.log.
Field Evaluator Issues:
Null checks in FieldEvaluator::getPropertyValue (fixed in v2.9.5) may cause silent failures. Verify with:
$evaluator = $container->get('massive_search.field_evaluator');
$value = $evaluator->getPropertyValue($entity, 'non_existent_field'); // Returns null
Expression Language Errors:
Use value() function for existence checks:
$query->where('value(attributes.tags) IS NOT NULL');
Custom Backends:
Implement Massive\SearchBundle\Search\Adapter\AdapterInterface for new search engines (e.g., Meilisearch).
Query Modifiers:
Extend Massive\SearchBundle\Search\Query\QueryBuilder to add custom clauses:
class CustomQueryBuilder extends QueryBuilder {
public function customBoost($field, $factor) {
$this->query['boost'] = [$field => $factor];
return $this;
}
}
Indexing Strategies:
Override Massive\SearchBundle\Indexer\IndexerInterface for custom indexing logic (e.g., async indexing).
Event Subscribers:
Modify search behavior via SearchEvent:
$eventDispatcher->addListener(SearchEvent::POST_SEARCH, function (SearchEvent $event) {
$event->getResults()->setTotalHits($event->getResults()->getTotalHits() * 2);
});
Elasticsearch 8 Compatibility:
Use compatibility mode in massive_search.yaml:
clients:
default:
type: elasticsearch
compatibility_mode: 7
Subfield Indexing: Enable via annotation:
/**
* @Search\Index(name="product", type="product")
* @Search\Field(name="tags", type="text", subfields=true)
*/
class Product {}
Memory Leaks:
Avoid serializing large entities. Use hydrate=false in queries:
$results = $searchManager->search('product', [
'query' => $query,
'hydrate' => false
]);
How can I help you explore Laravel packages today?