PHPackages                             symkit/search-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Search &amp; Filtering](/categories/search)
4. /
5. symkit/search-bundle

ActiveSymfony-bundle[Search &amp; Filtering](/categories/search)

symkit/search-bundle
====================

A standalone search bundle for Symfony applications.

v0.0.3(2mo ago)02313MITPHPPHP &gt;=8.2CI passing

Since Feb 21Pushed 2mo agoCompare

[ Source](https://github.com/SymKit/search-bundle)[ Packagist](https://packagist.org/packages/symkit/search-bundle)[ RSS](/packages/symkit-search-bundle/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (2)Dependencies (15)Versions (5)Used By (3)

Search Bundle
=============

[](#search-bundle)

[![CI](https://github.com/symkit/search-bundle/actions/workflows/ci.yml/badge.svg)](https://github.com/symkit/search-bundle/actions)[![Latest Version](https://camo.githubusercontent.com/70b491ac5ab2dc3cdd2da624830b69f4ecb89f1b2f0b5b5ab81f4369808db85b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f73796d6b69742f7365617263682d62756e646c652e737667)](https://packagist.org/packages/symkit/search-bundle)[![PHPStan Level 9](https://camo.githubusercontent.com/1bc07920f0d36e55c17e1d38b1caa132cc605f51a82b388c962870b9a747b898/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230392d627269676874677265656e2e737667)](https://phpstan.org/)

A modern, memory-efficient global search bundle for Symfony applications. Supports **multiple named search engines**, each with its own providers (or shared providers), and includes a ready-to-use UI built with Symfony UX Live Components and Tailwind CSS.

Features
--------

[](#features)

- **Multi-engine**: Define multiple search engines (`main`, `admin`, ...) with independent or shared providers.
- **Memory Efficient**: Uses PHP Generators (`yield`) to handle large result sets without memory spikes.
- **SOLID Architecture**: Decoupled providers, engines, and services with a Contract-first design.
- **Events**: `PreSearchEvent` and `PostSearchEvent` for query modification, result filtering, analytics.
- **Ready-to-use UI**: Accessible global search modal built with Symfony UX Live Components and Tailwind CSS.
- **Keyboard Navigation**: `Cmd+K` / `Ctrl+K` to open, arrow keys to navigate, `Enter` to select.
- **Multi-category**: Group results by category (Pages, Media, Routes, etc.) with custom priorities.

Requirements
------------

[](#requirements)

- PHP 8.2+
- Symfony 7.0+ or 8.0+

### Required for the UI component

[](#required-for-the-ui-component)

- [Tailwind CSS](https://tailwindcss.com/) for styling
- `symfony/ux-live-component` and `symfony/ux-twig-component` for the GlobalSearch component
- `symfony/ux-icons` for rendering icons
- `symfony/asset-mapper` for Stimulus controller auto-discovery

> The UI is **optional**. The search API works without any of the above.

Installation
------------

[](#installation)

```
composer require symkit/search-bundle
```

Install optional dependencies for the UI:

```
composer require symfony/ux-live-component symfony/ux-twig-component symfony/ux-icons
```

### Configure Assets (ImportMap)

[](#configure-assets-importmap)

Register the Stimulus controller in your `importmap.php`:

```
return [
    // ...
    'search/global-search-modal_controller' => [
        'path' => 'search/global-search-modal_controller.js',
    ],
];
```

### Tailwind CSS Integration

[](#tailwind-css-integration)

Add the bundle's templates to your Tailwind scan:

```
@import "tailwindcss";

@source "../../vendor/symkit/search-bundle/templates";
```

Configuration
-------------

[](#configuration)

### Single engine (default)

[](#single-engine-default)

With no configuration, a single `default` engine is created with UI enabled:

```
# config/packages/symkit_search.yaml
symkit_search: ~
```

### Multiple engines

[](#multiple-engines)

Define named engines, each with its own `ui` toggle:

```
# config/packages/symkit_search.yaml
symkit_search:
    default_engine: main      # explicit default (falls back to first declared)
    engines:
        main:
            ui: true          # GlobalSearch component enabled
        admin:
            ui: false         # API only, no UI component
```

- If `engines` is omitted, a single `default` engine with `ui: true` is created.
- `default_engine` sets which engine is aliased to `SearchServiceInterface`. Falls back to the first declared engine.
- Setting `engines: []` disables all search functionality.

Usage
-----

[](#usage)

### 1. Create a Search Provider

[](#1-create-a-search-provider)

Implement `SearchProviderInterface` and use the `#[AsSearchProvider]` attribute to assign it to an engine.

**Shared provider** (all engines):

```
use Symkit\SearchBundle\Attribute\AsSearchProvider;
use Symkit\SearchBundle\Contract\SearchProviderInterface;
use Symkit\SearchBundle\Model\SearchResult;

#[AsSearchProvider]
final readonly class PageSearchProvider implements SearchProviderInterface
{
    public function search(string $query): iterable
    {
        // yield SearchResult instances...
    }

    public function getCategory(): string
    {
        return 'Pages';
    }

    public function getPriority(): int
    {
        return 10;
    }
}
```

**Engine-specific provider**:

```
#[AsSearchProvider(engine: 'admin')]
final readonly class UserSearchProvider implements SearchProviderInterface
{
    // Only available in the "admin" engine
}
```

**Via YAML tags** (alternative to the attribute):

```
services:
    App\Search\MyProvider:
        tags:
            - { name: symkit_search.provider, engine: main }
```

Without the `engine` attribute, a provider is registered in **all** engines.

### 2. Add the UI Component

[](#2-add-the-ui-component)

Include the `GlobalSearch` Live Component in your Twig layout:

```
{# Default engine #}
{{ component('GlobalSearch') }}

{# Specific engine #}
{{ component('GlobalSearch', { engine: 'admin' }) }}
```

The component handles:

- **Keyboard Shortcuts**: `Cmd+K` / `Ctrl+K` to open.
- **Debounced Search**: Optimized typing experience.
- **Results Grouping**: Grouped by category, sorted by priority.
- **Accessibility**: ARIA roles, `aria-live` for screen readers.

### 3. Use the Search API Directly

[](#3-use-the-search-api-directly)

Inject the default engine or the registry:

```
use Symkit\SearchBundle\Contract\SearchServiceInterface;
use Symkit\SearchBundle\Contract\SearchEngineRegistryInterface;

final readonly class MyController
{
    public function __construct(
        private SearchServiceInterface $search,             // default engine
        private SearchEngineRegistryInterface $registry,    // all engines
    ) {}

    public function __invoke(): Response
    {
        // Default engine
        $results = $this->search->search('hello');

        // Specific engine
        $adminResults = $this->registry->get('admin')->search('hello');
    }
}
```

### 4. Listen to Search Events

[](#4-listen-to-search-events)

Hook into the search lifecycle with `PreSearchEvent` and `PostSearchEvent`:

```
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Symkit\SearchBundle\Event\PreSearchEvent;
use Symkit\SearchBundle\Event\PostSearchEvent;

#[AsEventListener]
final readonly class SearchAnalyticsListener
{
    public function __invoke(PostSearchEvent $event): void
    {
        // Log searches, filter results, add scoring, etc.
    }
}
```

`PreSearchEvent` lets you modify or cancel the query before providers are called. `PostSearchEvent` lets you filter, reorder, or enrich the results.

Architecture
------------

[](#architecture)

 ```
graph TD
    Config["symkit_search.engines"] --> Pass["SearchProviderPass"]
    Tags["#[AsSearchProvider] tags"] --> Pass
    Pass --> E1["SearchService (main)"]
    Pass --> E2["SearchService (admin)"]
    E1 --> R["SearchEngineRegistry"]
    E2 --> R
    R --> UI["GlobalSearch Component"]
    UI -->|"engine='main'"| E1
    UI -->|"engine='admin'"| E2
    E1 -.->|dispatch| PreSearch["PreSearchEvent"]
    E1 -.->|dispatch| PostSearch["PostSearchEvent"]
```

      Loading Advanced Customization
----------------------

[](#advanced-customization)

### Search Result Model

[](#search-result-model)

ParameterTypeDescription`title``string`The main text displayed for the result.`subtitle``string`Secondary information (e.g., path, category).`url``string`The destination link when clicked.`icon``string``ux_icon` identifier (e.g., `heroicons:photo`).`badge``?string`Optional badge text (e.g., status).### Overriding Templates

[](#overriding-templates)

Customize the search modal UI by creating:

```
templates/bundles/SymkitSearchBundle/components/GlobalSearch.html.twig

```

### Dedicated Exceptions

[](#dedicated-exceptions)

The bundle throws `EngineNotFoundException` (extends `InvalidArgumentException`) when requesting an unknown engine, making it easy to catch bundle-specific errors.

Contributing
------------

[](#contributing)

```
make install          # Install dependencies
make quality          # Run full quality pipeline (cs + phpstan + deptrac + tests + infection)
```

###  Health Score

38

—

LowBetter than 85% of packages

Maintenance83

Actively maintained with recent releases

Popularity11

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 92.3% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

3

Last Release

85d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/077eba6702dc23a795ee2262dff92505e3c8ead08f7cb205be80d8aae0a6b8e5?d=identicon)[sdieunidou](/maintainers/sdieunidou)

---

Top Contributors

[![sdieunidou](https://avatars.githubusercontent.com/u/570763?v=4)](https://github.com/sdieunidou "sdieunidou (12 commits)")[![google-labs-jules[bot]](https://avatars.githubusercontent.com/in/842251?v=4)](https://github.com/google-labs-jules[bot] "google-labs-jules[bot] (1 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/symkit-search-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/symkit-search-bundle/health.svg)](https://phpackages.com/packages/symkit-search-bundle)
```

###  Alternatives

[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.3M152](/packages/sulu-sulu)[prestashop/prestashop

PrestaShop is an Open Source e-commerce platform, committed to providing the best shopping cart experience for both merchants and customers.

9.0k15.4k](/packages/prestashop-prestashop)[scheb/2fa

Two-factor authentication for Symfony applications (please use scheb/2fa-bundle to install)

578630.7k1](/packages/scheb-2fa)[cmsig/seal-symfony-bundle

An integration of CMS-IG SEAL search abstraction into Symfony Framework.

15195.8k5](/packages/cmsig-seal-symfony-bundle)[open-dxp/opendxp

Content &amp; Product Management Framework (CMS/PIM)

7310.3k29](/packages/open-dxp-opendxp)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
