loupe/matcher
PHP library for search term highlighting and contextual snippet generation. Tokenize queries (phrases, negation, locale-aware rules), match terms with stop-word filtering and span positions, then format results with highlights and cropped excerpts for user-friendly search output.
Tokenizer → Matcher → Formatter) maps cleanly to Laravel’s service-oriented design, enabling reusable components.ext-intl) and advanced query syntax (phrases, negations), making it suitable for multilingual or complex search use cases. The pre-calculated matches feature aligns with Laravel’s caching mechanisms (e.g., Illuminate\Support\Facades\Cache).Cropper class enables standalone cropping of pre-highlighted text, useful for edge cases.<mark>, <em>, etc.) ensure compatibility with Blade templates, Livewire, or Inertia.js for dynamic rendering.^0.2.4) and monitor GitHub milestones for stabilization.ext-intl: Locale-aware tokenization requires the PHP intl extension. Mitigation: Document this requirement in composer.json and test environments.Tokenizer, Matcher, and Formatter as Laravel services for dependency injection.MatcherFacade to simplify usage (e.g., Matcher::highlight($text, $query)).LoupeMatcherServiceProvider.LIKE or full-text search results (e.g., Post::search($query)->withSnippets()).Cache::remember()) to avoid reprocessing identical queries.$snippet = Cache::remember("snippet:{$query}:{$documentId}", now()->addHours(1), function() use ($query, $document) {
return $formatter->format($document->body, $query, $options);
});
.highlight for styling).ext-intl is enabled (php -m | grep intl).symfony/options-resolver (already a Laravel dependency).Post::body, Product::description).ext-intl in php.ini and verify with php -m.composer require loupe/matcher.AppServiceProvider:
public function register()
{
$this->app->bind(Tokenizer::class, fn() => new Tokenizer('en_US'));
$this->app->bind(Matcher::class, fn($app) => new Matcher($app->make(Tokenizer::class)));
$this->app->bind(Formatter::class, fn($app) => new Formatter($app->make(Matcher::class)));
}
app/Facades/Matcher.php:
public static function highlight(string $text, string $query, ?FormatterOptions $options = null): string
{
return app(Formatter::class)->format($text, $query, $options)->getFormattedText();
}
use App\Facades\Matcher;
$snippet = Matcher::highlight(
$product->description,
'wireless earbuds',
(new FormatterOptions())
->withCropLength(150)
->withHighlightStartTag('<strong>')
);
loupe/matcher for breaking changes (watch GitHub releases).config/loupe-matcher.php).'default_options' => [
'crop_length' => 150,
'highlight_start_tag' => '<mark>',
'highlight_end_tag' => '</mark>',
],
Logger::debug($tokens->all())).if ($useFallback) {
return preg_replace(
"/($query)/i",
'<mark>$0</mark>',
$text
);
}
How can I help you explore Laravel packages today?