Installation
composer require demontpx/rigid-search-bundle
Ensure DemonTPx\RigidSearchBundle\DemonTPxRigidSearchBundle::class is registered in config/bundles.php (Symfony) or config/app.php (Laravel via bridge).
Define a Searchable Entity
Create a SearchDocumentExtractor class implementing SearchDocumentExtractorInterface for your entity (e.g., NewsItem).
Example:
use DemonTPx\RigidSearchBundle\Document\Document;
use DemonTPx\RigidSearchBundle\Document\SearchDocumentExtractorInterface;
class NewsItemDocumentExtractor implements SearchDocumentExtractorInterface {
public function extractDocument($item): Document {
$document = new Document(
$item->getTitle(),
$item->getDescription(),
$item->getPublishDate(),
route('news.show', $item)
);
$document->addField('title', $item->getTitle(), 10);
$document->addField('content', $item->getContent(), 5);
$document->addField('tags', implode(',', $item->getTags()), 3);
return $document;
}
}
Register the Extractor
Bind the extractor to your entity in the bundle’s configuration (config/packages/demontpx_rigid_search.yaml):
rigid_search:
extractors:
App\Entity\NewsItem: App\DocumentExtractor\NewsItemDocumentExtractor
Index Entities
Use the RigidSearchManager to index entities:
$manager = $this->container->get('rigid_search.manager');
$manager->indexEntity(new NewsItem());
Search Entities
$results = $manager->search('query', NewsItem::class);
Incremental Indexing Index entities in batches (e.g., during cron jobs) to avoid performance spikes:
$batch = $entityManager->getRepository(NewsItem::class)->findBy([], [], 100);
foreach ($batch as $item) {
$manager->indexEntity($item);
}
Real-Time Updates Trigger indexing after entity creation/updates via Doctrine lifecycle callbacks:
use Doctrine\ORM\Event\LifecycleEventArgs;
class NewsItem {
// ...
public function postPersist(LifecycleEventArgs $args) {
$manager = $args->getEntityManager()->getService('rigid_search.manager');
$manager->indexEntity($this);
}
}
Hybrid Search Combine with Laravel Scout or Algolia for multi-layered search:
// Fallback to rigid-search if Scout fails
try {
$results = $entity->search('query');
} catch (\Exception $e) {
$results = $manager->search('query', $entity::class);
}
Custom Relevance Logic
Override Document::addField() weights dynamically (e.g., boost recent items):
$weight = $item->getPublishDate()->diff(new \DateTime())->days < 7 ? 15 : 5;
$document->addField('title', $item->getTitle(), $weight);
Multi-Entity Search Search across multiple entity types by chaining results:
$newsResults = $manager->search('query', NewsItem::class);
$articleResults = $manager->search('query', Article::class);
$results = array_merge($newsResults, $articleResults);
Pagination
Use Document::setPageSize() and setPage() for client-side pagination:
$manager->search('query', NewsItem::class, 1, 10); // Page 1, 10 items
Field Weighting
10 vs. 5 means the first field is twice as important, not "10x better."Case Sensitivity
strtolower() in extractDocument() for case-insensitive searches:
$document->addField('title', strtolower($item->getTitle()), 10);
Memory Limits
URL Generation
Document break in multi-environment setups.route() or Symfony’s UrlGeneratorInterface:
$this->generateUrl($item) => route('news.show', $item);
No Built-in Full-Text Search
Verify Indexed Data Dump the extracted document to debug field weights and content:
$extractor = new NewsItemDocumentExtractor();
$document = $extractor->extractDocument($newsItem);
dd($document->getFields());
Check Search Queries Log raw search queries to validate input:
$manager->search('query', NewsItem::class, 1, 10, true); // Enable debug mode
Custom Scoring
Extend Document to add custom scoring logic:
class CustomDocument extends Document {
public function calculateRelevance($query) {
$baseScore = parent::calculateRelevance($query);
// Add custom logic (e.g., boost for featured items)
return $baseScore * ($this->isFeatured() ? 1.5 : 1);
}
}
Plugin System
Create a SearchPluginInterface to add pre/post-processing:
interface SearchPluginInterface {
public function preSearch(array &$options);
public function postSearch(array &$results);
}
Laravel Bridge For Laravel, publish the config and create a service provider to bind the manager:
// config/services.php
'rigid_search.manager' => \DemonTPx\RigidSearchBundle\Manager\RigidSearchManager::class,
// app/Providers/AppServiceProvider.php
public function register() {
$this->app->bind('rigid_search.manager', function ($app) {
return new \DemonTPx\RigidSearchBundle\Manager\RigidSearchManager(
$app['rigid_search.extractor_registry'],
$app['config']['rigid_search']
);
});
}
Caching Cache search results for high-traffic queries:
$cacheKey = 'search:news:' . md5('query' . NewsItem::class);
$results = Cache::remember($cacheKey, 3600, function () use ($manager) {
return $manager->search('query', NewsItem::class);
});
How can I help you explore Laravel packages today?