### Development Setup Commands (Bash)
Source: https://github.com/mezcalito/ux-search/blob/0.x/README.md
Provides essential bash commands for setting up the development environment for the Mezcalito UX Search project. This includes cloning the repository, starting Docker, installing dependencies, running tests, and performing code quality checks.
```bash
# Clone the repository
git clone https://github.com/mezcalito/ux-search.git
cd ux-search
# Start the Docker development environment
make up
# Install dependencies
make install
# Run tests
make test
# Run code quality checks
make ci
```
--------------------------------
### Complete Product Search Example with Meilisearch
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/meilisearch.md
A comprehensive PHP example demonstrating a ProductSearch class using the Meilisearch adapter. It configures adapter parameters for cropping descriptions, highlighting search terms, and specifying retrieved attributes. Additionally, it sets up facets, available hits per page, sorting options, and enables URL rewriting.
```php
setAdapterParameters([
// Crop long descriptions
MeilisearchAdapter::ATTRIBUTES_TO_CROP_PARAM => ['description'],
MeilisearchAdapter::CROP_LENGTH_PARAM => 20,
MeilisearchAdapter::CROP_MARKER_PARAM => '...',
// Highlight search terms
MeilisearchAdapter::ATTRIBUTES_TO_HIGHLIGHT_PARAM => ['name', 'description'],
MeilisearchAdapter::HIGHLIGHT_PRE_TAG_PARAM => '',
MeilisearchAdapter::HIGHLIGHT_POST_TAG_PARAM => '',
// Retrieve specific fields only
MeilisearchAdapter::ATTRIBUTES_TO_RETRIEVE_PARAM => [
'id', 'name', 'description', 'price', 'image', 'brand'
],
])
->addFacet('type', 'Product Type', null, ['limit' => 10])
->addFacet('brand', 'Brand')
->addFacet('rating', 'Rating')
->addFacet('price_range', 'Price Range')
->addFacet('price', 'Price', RangeSlider::class)
->setAvailableHitsPerPage([12, 24, 48])
->addAvailableSort(null, 'Relevance')
->addAvailableSort('price:asc', 'Price ↑')
->addAvailableSort('price:desc', 'Price ↓')
->addAvailableSort('popularity:desc', 'Most Popular')
->enableUrlRewriting()
;
}
}
```
--------------------------------
### CSS for Custom Search Highlight Styling
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/algolia.md
Example CSS to style the custom search highlights defined using the 'search-highlight' class.
```css
.search-highlight {
background-color: yellow;
font-weight: bold;
}
```
--------------------------------
### Install Mezcalito UX Search with Composer
Source: https://github.com/mezcalito/ux-search/blob/0.x/README.md
This command installs the Mezcalito UX Search bundle using Composer. It is the standard method for adding the library to your Symfony project.
```bash
composer require mezcalito/ux-search
```
--------------------------------
### Complete Product Search Class with Algolia Adapter (PHP)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/algolia.md
This example demonstrates a complete ProductSearch class extending AbstractSearch, configuring the Algolia adapter with custom highlighting, facets, sorting, and URL rewriting.
```php
setAdapterParameters([
// Customize highlighting tags
AlgoliaAdapter::ATTRIBUTES_TO_HIGHLIGHT_PARAM => ['name', 'description'],
AlgoliaAdapter::HIGHLIGHT_PRE_TAG_PARAM => '',
AlgoliaAdapter::HIGHLIGHT_POST_TAG_PARAM => '',
// Limit facet values
AlgoliaAdapter::MAX_VALUES_PER_FACET_PARAM => 50,
// Enable analytics with tags
AlgoliaAdapter::ANALYTICS_PARAM => true,
AlgoliaAdapter::ANALYTICS_TAGS_PARAM => ['web', 'products'],
])
->addFacet('type', 'Product Type', null, ['limit' => 10])
->addFacet('brand', 'Brand')
->addFacet('rating', 'Rating')
->addFacet('price', 'Price', RangeSlider::class)
->addAvailableSort('products_index', 'Relevance')
->addAvailableSort('products_index_price_asc', 'Price ↑')
->addAvailableSort('products_index_price_desc', 'Price ↓')
->setAvailableHitsPerPage([12, 24, 48])
->enableUrlRewriting()
;
}
}
```
--------------------------------
### Complete Product Search with Doctrine Adapter
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/doctrine.md
An example of a complete `ProductSearch` class utilizing the Doctrine adapter. It configures search fields, facets, sorting, and filtering for active products.
```php
setAdapterParameters([
DoctrineAdapter::SEARCH_FIELDS => ['o.name', 'o.brand', 'o.description'],
DoctrineAdapter::QUERY_BUILDER_ALIAS => 'o',
DoctrineAdapter::QUERY_BUILDER => function (QueryBuilder $qb) {
// Only show active products
$qb->andWhere('o.active = :active')
->setParameter('active', true);
},
DoctrineAdapter::MAX_FACET_VALUES_PARAM => 30,
])
->addFacet('o.category', 'Category')
->addFacet('o.brand', 'Brand')
->addFacet('o.price', 'Price', RangeInput::class)
->addAvailableSort('o.price:asc', 'Price ↑')
->addAvailableSort('o.price:desc', 'Price ↓')
->addAvailableSort('o.createdAt:desc', 'Newest')
->setAvailableHitsPerPage([12, 24, 48])
;
}
}
```
--------------------------------
### Set Mezcalito UX Search DSN in .env File
Source: https://github.com/mezcalito/ux-search/blob/0.x/README.md
This shows examples of how to configure the Mezcalito UX Search DSN in your project's .env file, depending on the adapter you choose (Algolia, Meilisearch, or Doctrine).
```bash
# For Algolia
MEZCALITO_UX_SEARCH_DEFAULT_DSN=algolia://YOUR_API_KEY@YOUR_APP_ID
# For Meilisearch
MEZCALITO_UX_SEARCH_DEFAULT_DSN=meilisearch://YOUR_MASTER_KEY@localhost:7700
# For Doctrine ORM
MEZCALITO_UX_SEARCH_DEFAULT_DSN=doctrine://default
```
--------------------------------
### Optimize Facets with Limit Configuration
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/meilisearch.md
Improve Meilisearch performance by optimizing facet retrieval. This example shows how to limit the number of facet values returned for a specific attribute (e.g., 'brand') to 20, reducing the complexity and size of facet data.
```php
->addFacet('brand', 'Brand', null, ['limit' => 20]) // Limit facet values
```
--------------------------------
### Implement UX Search Adapter Factory Interface (PHP)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/create-own-adapter.md
This code implements the `AdapterFactoryInterface` for creating adapter instances from DSN strings. The `support` method checks if the factory can handle a given DSN, and `createAdapter` instantiates and returns the appropriate adapter. This is crucial for the bundle to dynamically create and use custom adapters.
```php
'onPreSearch',
];
}
public function onPreSearch(PreSearchEvent $event): void
{
$query = $event->getQuery();
// Modify the query before search execution
$query->addFilter('status', 'published');
}
}
```
--------------------------------
### Create a ResultSet Object (PHP)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/create-own-adapter.md
This PHP code shows how to construct a `ResultSet` object, which aggregates all search results and related metadata. It includes methods to set the total number of results, the array of `Hit` objects, and collections of facet distributions and statistics.
```php
use Mezcalito\UxSearchBundle\Search\ResultSet\ResultSet;
$resultSet = new ResultSet();
$resultSet->setTotalResults(150);
$resultSet->setHits($hits);
$resultSet->setFacetDistributions($facetDistributions);
$resultSet->setFacetStats($facetStats);
```
--------------------------------
### Generate a Search Class with Maker Command
Source: https://github.com/mezcalito/ux-search/blob/0.x/README.md
This command uses the Symfony MakerBundle to generate a new search class. It prompts for index name, search name, and adapter, simplifying initial setup.
```bash
php bin/console make:search
```
--------------------------------
### Render Mezcalito UX Search in Twig Template
Source: https://github.com/mezcalito/ux-search/blob/0.x/README.md
These examples demonstrate how to render the Mezcalito UX Search component in a Twig template, using both the Twig component syntax and the component function.
```twig
{# Using Twig component syntax #}
{# Or using component function #}
{{ component('Mezcalito:UxSearch:Layout', { name: 'product' }) }}
```
--------------------------------
### Set Mezcalito UX Search Adapter DSN in .env
Source: https://context7.com/mezcalito/ux-search/llms.txt
Example environment variable configurations for Mezcalito UX Search's default adapter DSN. Choose one adapter (Algolia, Meilisearch, or Doctrine ORM) based on your needs.
```bash
# Algolia
MEZCALITO_UX_SEARCH_DEFAULT_DSN=algolia://YOUR_API_KEY@YOUR_APP_ID
# Meilisearch
MEZCALITO_UX_SEARCH_DEFAULT_DSN=meilisearch://YOUR_MASTER_KEY@localhost:7700
# Doctrine ORM
MEZCALITO_UX_SEARCH_DEFAULT_DSN=doctrine://default
```
--------------------------------
### Highlight Search Terms in Meilisearch Results
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/meilisearch.md
Configure adapter parameters to highlight matching search terms within Meilisearch results. This involves specifying which attributes to highlight and defining the pre and post tags to be applied around the matching terms. The provided CSS example shows how to style these highlighted terms.
```php
->setAdapterParameters([
MeilisearchAdapter::ATTRIBUTES_TO_HIGHLIGHT_PARAM => ['name', 'description', 'brand'],
MeilisearchAdapter::HIGHLIGHT_PRE_TAG_PARAM => '',
MeilisearchAdapter::HIGHLIGHT_POST_TAG_PARAM => '',
])
```
```css
.text-yellow-400 {
background-color: #ffd700;
font-weight: 600;
padding: 0 2px;
}
```
--------------------------------
### Use Custom Adapter in a Search Class (PHP)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/create-own-adapter.md
This PHP code demonstrates how to use a custom adapter within a search class by leveraging the `#[AsSearch]` attribute. The `adapter` parameter in the attribute specifies which adapter to use for this search. The `build` method is where custom logic for the search can be implemented.
```php
1, 'name' => 'Product', 'price' => 29.99],
score: 0.95
);
```
--------------------------------
### Elasticsearch Adapter Factory in PHP
Source: https://context7.com/mezcalito/ux-search/llms.txt
This PHP code defines a factory class for creating Elasticsearch adapters. It implements the AdapterFactoryInterface and supports DSNs starting with 'elasticsearch://'. The factory parses the DSN to extract host and API key, then instantiates the ElasticsearchAdapter.
```php
// Factory class
namespace App\Search\Adapter;
use Mezcalito\UxSearchBundle\Adapter\AdapterFactoryInterface;
use Mezcalito\UxSearchBundle\Adapter\AdapterInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
final readonly class ElasticsearchAdapterFactory implements AdapterFactoryInterface
{
public function __construct(private HttpClientInterface $client)
{
}
public function support(string $dsn): bool
{
return str_starts_with($dsn, 'elasticsearch://');
}
public function createAdapter(string $dsn): AdapterInterface
{
// Parse DSN: elasticsearch://apikey@localhost:9200
$parsed = parse_url($dsn);
$host = sprintf('%s://%s:%d', 'https', $parsed['host'], $parsed['port'] ?? 9200);
$apiKey = $parsed['user'] ?? '';
return new ElasticsearchAdapter($this->client, $host, $apiKey);
}
}
```
--------------------------------
### Implement UX Search Adapter Interface (PHP)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/create-own-adapter.md
This code defines a custom adapter class that implements the `AdapterInterface` for the UX Search Bundle. It includes the `search` method for performing search operations and `configureParameters` for defining settable adapter parameters. Dependencies include the UX Search Bundle's core interfaces and Symfony's OptionsResolver.
```php
setDefaults([
'my_param' => 'my_value',
]);
$resolver->setAllowedTypes('my_param', 'string');
}
}
```
--------------------------------
### Configure UX Search Bundle with Custom Adapter (YAML)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/create-own-adapter.md
This YAML configuration snippet shows how to register a custom adapter with the UX Search Bundle. It sets a default adapter and defines an alias ('myAdapter') pointing to the DSN for the custom adapter. This allows the bundle to use the newly created adapter.
```yaml
# config/packages/mezcalito_ux_search.yaml
mezcalito_ux_search:
default_adapter: 'myAdapter'
adapters:
myAdapter: 'myAdapter://what_you_need'
```
--------------------------------
### Modify Algolia Search Results with PostSearchEvent (PHP)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/algolia.md
Integrate with the PostSearchEvent to modify Algolia search results after they are fetched. This example shows how to add computed fields and URLs to each hit.
```php
use Mezcalito\UxSearchBundle\Event\PostSearchEvent;
->addEventListener(PostSearchEvent::class, function (PostSearchEvent $event) {
foreach ($event->getResultSet()->getHits() as $hit) {
$data = $hit->getData();
// Add computed fields
$data['discount_percentage'] = $this->calculateDiscount($data);
// Add URLs
$data['url'] = $this->router->generate('product_show', [
'id' => $data['objectID']
]);
$hit->setData($data);
}
}, priority: 10)
```
--------------------------------
### Combine Highlighting and Cropping for Search Previews
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/meilisearch.md
Create effective search result page previews by combining text highlighting and cropping. This Meilisearch adapter configuration highlights matching terms and crops long content to show relevant excerpts, while also optimizing the retrieved fields for efficiency.
```php
->setAdapterParameters([
// Highlight matching terms
MeilisearchAdapter::ATTRIBUTES_TO_HIGHLIGHT_PARAM => ['title', 'content'],
MeilisearchAdapter::HIGHLIGHT_PRE_TAG_PARAM => '',
MeilisearchAdapter::HIGHLIGHT_POST_TAG_PARAM => '',
// Crop long content to show relevant excerpt
MeilisearchAdapter::ATTRIBUTES_TO_CROP_PARAM => ['content'],
MeilisearchAdapter::CROP_LENGTH_PARAM => 30,
MeilisearchAdapter::CROP_MARKER_PARAM => '…',
// Only retrieve what's needed
MeilisearchAdapter::ATTRIBUTES_TO_RETRIEVE_PARAM => [
'id', 'title', 'content', 'author', 'created_at'
],
])
```
--------------------------------
### UX Search Configuration Parameters
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/algolia.md
This section lists and describes the various configuration parameters available for the UX Search functionality. These parameters allow for fine-tuning search behavior, result presentation, and analytics.
```APIDOC
## Available Configuration Parameters
This endpoint provides information about the configurable parameters for UX Search.
### Method
GET
### Endpoint
/configuration/ux-search
### Parameters
#### Query Parameters
None
#### Request Body
None
### Response
#### Success Response (200)
- **parameter_name** (string) - The name of the configuration parameter.
- **algolia_name** (string) - The corresponding name used in Algolia.
- **type** (string) - The data type of the parameter (e.g., bool, string, string[]).
- **default_value** (any) - The default value of the parameter.
- **description** (string) - A description of what the parameter controls.
#### Response Example
```json
{
"parameters": [
{
"constant_name": "ADVANCED_SYNTAX_PARAM",
"algolia_name": "advancedSyntax",
"type": "bool",
"default_value": false,
"description": "Enable advanced search syntax (AND, OR, NOT operators)"
},
{
"constant_name": "ADVANCED_SYNTAX_FEATURES_PARAM",
"algolia_name": "advancedSyntaxFeatures",
"type": "string[]",
"default_value": [
"exactPhrase",
"excludeWords"
],
"description": "Control which advanced syntax features are enabled"
}
// ... other parameters
]
}
```
### ALTERNATIVES_AS_EXACT_PARAM Values
This parameter accepts the following values:
- `'ignorePlurals'` - Treat plural/singular as exact matches
- `'singleWordSynonym'` - Treat single-word synonyms as exact matches
- `'multiWordsSynonym'` - Treat multi-word synonyms as exact matches
- `'ignorePlurals'` - Same as above (can be used multiple times)
```
--------------------------------
### Customize Hit Template (Twig)
Source: https://github.com/mezcalito/ux-search/blob/0.x/README.md
Overrides the default hit template to customize how search results are displayed. This example shows how to render product information including image, name, price, and a link to product details.
```twig
{# templates/components/Mezcalito/UxSearch/Hits.html.twig #}
{% for hit in this.resultSet.hits %}
{{ hit.name }}
{{ hit.price|format_currency('EUR') }}
View details
{% endfor %}
```
--------------------------------
### Define Multiple Search Configurations (PHP)
Source: https://github.com/mezcalito/ux-search/blob/0.x/README.md
Shows how to configure multiple, distinct search instances within a single application using attributes. Each `AbstractSearch` subclass can be configured with a different index and adapter (e.g., Algolia, Meilisearch, Doctrine ORM).
```php
// Product search with Algolia
#[AsSearch(index: 'products', adapter: 'algolia')]
class ProductSearch extends AbstractSearch { }
// Blog search with Meilisearch
#[AsSearch(index: 'posts', adapter: 'meilisearch')]
class BlogSearch extends AbstractSearch { }
// User search with Doctrine
#[AsSearch(index: 'App\Entity\User', adapter: 'orm')]
class UserSearch extends AbstractSearch { }
```
--------------------------------
### Configure Meilisearch Adapter
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/meilisearch.md
Configure the Meilisearch adapter for the UX Search Bundle by specifying the Meilisearch connection URL. This includes providing the API key and host information, with an option for HTTPS in production environments. Ensure the API key is obtained from the Meilisearch dashboard or use the master key for development.
```yaml
# config/packages/mezcalito_ux_search.yaml
mezcalito_ux_search:
adapters:
meilisearch: 'meilisearch://YOUR_API_KEY@localhost:7700'
# Or for production with HTTPS:
# meilisearch: 'meilisearch://YOUR_API_KEY@search.example.com:443'
```
--------------------------------
### Custom Product Display in Hits Component (Twig)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/components/Hits.md
This example demonstrates how to customize the rendering of individual search results by overriding the 'hit' block. It shows a basic product card structure with image, title, price, and a link.
```twig
{% extends '@MezcalitoUxSearch/Hits.html.twig' %}
{% block hit %}
{% endblock %}
```
--------------------------------
### Render Mezcalito UX Search Layout Component in Twig
Source: https://context7.com/mezcalito/ux-search/llms.txt
Twig template examples demonstrating how to render the Mezcalito UX Search Layout component. It shows the basic usage with the component name and how to pass additional options.
```twig
{# Using Twig component syntax #}
{# Or using component function #}
{{ component('Mezcalito:UxSearch:Layout', { name: 'product' }) }}
{# With additional options passed to build() method #}
```
--------------------------------
### PHP Product Search Implementation with UX Search
Source: https://context7.com/mezcalito/ux-search/llms.txt
Demonstrates a full-featured search class for products using the UX Search bundle in PHP. It configures a Doctrine adapter, defines multiple facets (category, brand, price, rating), sets available sorting options, configures pagination, and implements pre-search and post-search event listeners for logging, access control, and result enrichment.
```php
setAdapterParameters([
DoctrineAdapter::SEARCH_FIELDS => ['p.name', 'p.description', 'p.brand'],
DoctrineAdapter::QUERY_BUILDER_ALIAS => 'p',
DoctrineAdapter::QUERY_BUILDER => function (QueryBuilder $qb) use ($options) {
$qb->andWhere('p.active = :active')
->andWhere('p.stock > :minStock')
->setParameter('active', true)
->setParameter('minStock', 0);
// Apply category filter from options
if (!empty($options['category'])) {
$qb->andWhere('p.category = :category')
->setParameter('category', $options['category']);
}
},
DoctrineAdapter::MAX_FACET_VALUES_PARAM => 30,
])
// Facets configuration
->addFacet('p.category', 'Category', null, ['limit' => 10])
->addFacet('p.brand', 'Brand', null, ['limit' => 20])
->addFacet('p.color', 'Color', null, ['limit' => 8])
->addFacet('p.price', 'Price', RangeSlider::class)
->addFacet('p.rating', 'Rating', RangeInput::class)
// Sorting options
->addAvailableSort(null, 'Relevance')
->addAvailableSort('p.price:asc', 'Price: Low to High')
->addAvailableSort('p.price:desc', 'Price: High to Low')
->addAvailableSort('p.rating:desc', 'Best Rated')
->addAvailableSort('p.createdAt:desc', 'Newest First')
->addAvailableSort('p.popularity:desc', 'Most Popular')
// Pagination
->setAvailableHitsPerPage([12, 24, 48, 96])
// Pre-search: logging and access control
->addEventListener(PreSearchEvent::class, function (PreSearchEvent $event) {
$query = $event->getQuery();
$this->logger->info('Product search', [
'query' => $query->getQueryString(),
'user' => $this->security->getUser()?->getUserIdentifier(),
'filters' => count($query->getActiveFilters()),
]);
// Premium users see exclusive products
if ($this->security->isGranted('ROLE_PREMIUM')) {
$filter = new TermFilter('p.exclusive');
$filter->setValues(['1']);
$query->addActiveFilter($filter);
}
}, priority: 10)
// Post-search: enrich results
->addEventListener(PostSearchEvent::class, function (PostSearchEvent $event) {
$resultSet = $event->getResultSet();
// Batch fetch stock data
$ids = array_map(fn($hit) => $hit->getData()['id'], $resultSet->getHits());
$stockData = $this->productRepository->getStockByIds($ids);
foreach ($resultSet->getHits() as $hit) {
$data = $hit->getData();
// Generate URLs
$data['url'] = $this->router->generate('product_show', ['id' => $data['id']]);
// Stock information
$data['in_stock'] = ($stockData[$data['id']] ?? 0) > 0;
// Discount calculation
if (isset($data['originalPrice']) && $data['price'] < $data['originalPrice']) {
$data['discount'] = sprintf('-%d%%', round((1 - $data['price'] / $data['originalPrice']) * 100));
}
$hit->setData($data);
}
});
}
}
```
--------------------------------
### Integrate Post-Search Events for Result Modification
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/meilisearch.md
Modify search results after they are retrieved from Meilisearch using event listeners. This PHP example demonstrates how to add computed fields, generate URLs, and update hit data within a `PostSearchEvent` listener, allowing for dynamic result manipulation.
```php
use Mezcalito\UxSearchBundle\Event\PostSearchEvent;
->addEventListener(PostSearchEvent::class, function (PostSearchEvent $event) {
foreach ($event->getResultSet()->getHits() as $hit) {
$data = $hit->getData();
// Add computed fields
$data['discount'] = $this->calculateDiscount($data['price']);
$data['in_stock'] = $this->checkStock($data['id']);
// Generate URLs
$data['url'] = $this->router->generate('product_show', [
'slug' => $data['slug']
]);
$hit->setData($data);
}
}, priority: 2)
```
--------------------------------
### Create a FacetStat Object (PHP)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/create-own-adapter.md
This PHP snippet illustrates creating a `FacetStat` object, used for numeric range facets such as price or rating. It defines the property, the overall minimum and maximum values in the dataset, and optionally the user's selected range.
```php
use Mezcalito\UxSearchBundle\Search\ResultSet\FacetStat;
$stat = new FacetStat(
property: 'price',
min: 0, // Minimum value in dataset
max: 999, // Maximum value in dataset
userMin: 10, // User selected minimum (optional)
userMax: 500, // User selected maximum (optional)
);
```
--------------------------------
### Configure UX Search Facets with PHP
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/components/Facet/RefinementList.md
This PHP code demonstrates how to configure facets for the UX Search component within a custom search class. It uses the `addFacet` method to define facet properties like the attribute, label, and optional configuration such as the display limit.
```php
use Mezcalito\UxSearchBundle\Search\AbstractSearch;
#[AsSearch('products')]
class ProductSearch extends AbstractSearch
{
public function build(array $options = []): void
{
$this
->addFacet('brand', 'Brand')
->addFacet('category', 'Category')
->addFacet('color', 'Color', null, ['limit' => 10]) // Show max 10 values
->addFacet('size', 'Size', null, ['limit' => 5]);
}
}
```
--------------------------------
### Create a FacetTermDistribution Object (PHP)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/create-own-adapter.md
This PHP code demonstrates creating a `FacetTermDistribution` object, used for facets with discrete values like brands or categories. It allows setting the property name, mapping values to counts, and indicating which values are currently selected by the user.
```php
use Mezcalito\UxSearchBundle\Search\ResultSet\FacetTermDistribution;
$facet = new FacetTermDistribution();
$facet->setProperty('brand');
$facet->setValues([
'Nike' => 45, // 45 products
'Adidas' => 32, // 32 products
'Puma' => 18, // 18 products
]);
$facet->setCheckedValues(['Nike']); // User selected Nike
```
--------------------------------
### Configuring Sort Options (PHP)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/components/SortBy.md
This PHP code demonstrates how to define available sort options within a custom Search class by extending `AbstractSearch` and using the `addAvailableSort` method.
```php
use Mezcalito\UxSearchBundle\Search\AbstractSearch;
#[AsSearch('products')]
class ProductSearch extends AbstractSearch
{
public function build(array $options = []): void
{
$this
->addAvailableSort('relevance', 'Relevance')
->addAvailableSort('price:asc', 'Price: Low to High')
->addAvailableSort('price:desc', 'Price: High to Low')
->addAvailableSort('name:asc', 'Name: A to Z')
->addAvailableSort('created:desc', 'Newest First')
->addAvailableSort('rating:desc', 'Top Rated')
;
}
}
```
--------------------------------
### Render RefinementList in Loop (Twig)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/components/Facet/RefinementList.md
Shows how to iterate over available facets and render the RefinementList component for each one using the generic Facet component. This allows dynamic rendering of multiple refinement lists.
```twig
{% for facet in search.facets %}
{% endfor %}
```
--------------------------------
### Optimize Search Performance with Facet Limits and Hits per Page (PHP)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/customize-your-search.md
This snippet demonstrates how to optimize search performance in Mezcalito by limiting the number of facet values returned and configuring available 'hits per page' options. It also shows an example of enriching search results efficiently within a PostSearchEvent, fetching only necessary data to reduce overhead.
```php
// Limit facet values to improve performance
->addFacet('brand', 'Brand', null, ['limit' => 20])
// Use appropriate hits per page options
->setAvailableHitsPerPage([12, 24, 48]) // Don't offer 100+
// In PostSearchEvent, only enrich necessary data
->addEventListener(PostSearchEvent::class, function (PostSearchEvent $event) {
// Only fetch what's needed, not full entities
$ids = array_map(fn($hit) => $hit->getData()['id'], $event->getResultSet()->getHits());
$urls = $this->urlRepository->findUrlsByProductIds($ids); // Optimized query
// Map URLs to hits
foreach ($event->getResultSet()->getHits() as $hit) {
$data = $hit->getData();
$data['url'] = $urls[$data['id']] ?? '/';
$hit->setData($data);
}
})
```
--------------------------------
### Wise Use of Cropping for Shorter Excerpts
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/meilisearch.md
Implement efficient text cropping in Meilisearch results by setting a concise `CROP_LENGTH_PARAM`. This PHP configuration limits the description to 15 words, ensuring brief and focused previews in search results.
```php
->setAdapterParameters([
MeilisearchAdapter::ATTRIBUTES_TO_CROP_PARAM => ['description'],
MeilisearchAdapter::CROP_LENGTH_PARAM => 15, // Keep it short
])
```
--------------------------------
### Enable Algolia A/B Testing for Search Configurations (PHP)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/algolia.md
Configure Algolia to enable A/B testing for search parameters, allowing you to test different ranking formulas, synonym configurations, or facet orderings.
```php
->setAdapterParameters([
AlgoliaAdapter::ENABLE_AB_TEST_PARAM => true,
])
```
--------------------------------
### Create SEO-Friendly URL Formatter in PHP
Source: https://context7.com/mezcalito/ux-search/llms.txt
This PHP class implements the UrlFormaterInterface to generate SEO-friendly URLs from search queries and apply filters from rewritten URLs back to the query object. It handles query strings, term filters, range filters, sorting, and pagination. Dependencies include classes from the MezcalitoUxSearchBundle.
```php
getQueryString()) {
$parts[] = 'q/' . urlencode($queryString);
}
foreach ($query->getActiveFilters() as $filter) {
$property = str_replace('.', '-', $filter->getProperty());
if ($filter instanceof TermFilter) {
$values = implode(',', $filter->getValues());
$parts[] = $property . '/' . urlencode($values);
} elseif ($filter instanceof RangeFilter) {
$parts[] = $property . '/' . $filter->getMin() . '-' . $filter->getMax();
}
}
if ($sort = $query->getActiveSort()) {
$parts[] = 'sort/' . urlencode($sort);
}
if ($query->getCurrentPage() > 1) {
$parts[] = 'page/' . $query->getCurrentPage();
}
return $request->getPathInfo() . '/' . implode('/', $parts);
// Output: /products/q/laptop/brand/Dell,HP/price/500-1500/sort/price:asc/page/2
}
public function applyFilters(CurrentRequest $request, SearchInterface $search, Query $query): void
{
$path = trim($request->getPathInfo(), '/');
$segments = explode('/', $path);
// Skip base path segments
$segments = array_slice($segments, 1);
for ($i = 0; $i < count($segments); $i += 2) {
$key = $segments[$i] ?? null;
$value = $segments[$i + 1] ?? null;
if (!$key || !$value) continue;
match ($key) {
'q' => $query->setQueryString(urldecode($value)),
'page' => $query->setCurrentPage((int) $value),
'sort' => $query->setActiveSort(urldecode($value)),
'brand', 'category' => (function () use ($query, $key, $value) {
$filter = new TermFilter(str_replace('-', '.', $key));
$filter->setValues(explode(',', urldecode($value)));
$query->addActiveFilter($filter);
})(),
'price' => (function () use ($query, $value) {
[$min, $max] = explode('-', $value);
$filter = new RangeFilter('price');
$filter->setMin((float) $min);
$filter->setMax((float) $max);
$query->addActiveFilter($filter);
})(),
default => null,
};
}
}
}
```
--------------------------------
### Configure Product Search Class in PHP
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/usage/customize-your-search.md
Demonstrates a fully configured Search class for products, including adapter settings, facets, sorting, pagination, event listeners for pre- and post-search actions, and URL rewriting.
```php
setAdapterParameters([
DoctrineAdapter::SEARCH_FIELDS => ['p.name', 'p.description', 'p.brand'],
DoctrineAdapter::QUERY_BUILDER_ALIAS => 'p',
DoctrineAdapter::QUERY_BUILDER => function (QueryBuilder $qb) {
$qb->andWhere('p.active = :active')
->andWhere('p.stock > :min_stock')
->setParameter('active', true)
->setParameter('min_stock', 0);
},
])
// Facets
->addFacet('p.category', 'Category', null, ['limit' => 10])
->addFacet('p.brand', 'Brand', null, ['limit' => 20])
->addFacet('p.rating', 'Rating')
->addFacet('p.price', 'Price', RangeSlider::class)
->addFacet('p.weight', 'Weight (kg)', RangeInput::class)
// Sorting options
->addAvailableSort(null, 'Relevance')
->addAvailableSort('p.price:asc', 'Price: Low to High')
->addAvailableSort('p.price:desc', 'Price: High to Low')
->addAvailableSort('p.rating:desc', 'Best Rated')
->addAvailableSort('p.createdAt:desc', 'Newest First')
// Pagination
->setAvailableHitsPerPage([12, 24, 48, 96])
// Pre-search event: Log queries
->addEventListener(PreSearchEvent::class, function (PreSearchEvent $event) {
$this->logger->info('Product search', [
'query' => $event->getQuery()->getQuery(),
'user' => $this->security->getUser()?->getEmail(),
]);
// Add filter for premium users
if ($this->security->isGranted('ROLE_PREMIUM')) {
$event->getQuery()->addFilter('p.exclusive', ['true']);
}
}, priority: 10)
// Post-search event: Enrich results
->addEventListener(PostSearchEvent::class, function (PostSearchEvent $event) {
foreach ($event->getResultSet()->getHits() as $hit) {
$data = $hit->getData();
// Generate URL
$data['url'] = $this->router->generate('product_show', [
'id' => $data['id']
]);
// Add discount badge
if ($data['discount'] > 0) {
$data['badge'] = sprintf('-%d%%', $data['discount']);
}
$hit->setData($data);
}
}, priority: 5)
// URL rewriting
->enableUrlRewriting()
;
}
}
```
--------------------------------
### Render Pagination Component (Twig)
Source: https://github.com/mezcalito/ux-search/blob/0.x/docs/components/Pagination.md
Demonstrates how to render the Pagination component within a Twig template. This is the primary method for integrating the pagination system into your application's UI. No external dependencies are required beyond the component itself.
```twig
```
--------------------------------
### Implement Search Analytics with Event Subscriber (PHP)
Source: https://context7.com/mezcalito/ux-search/llms.txt
This snippet shows how to create a PHP event subscriber to track search analytics. It listens for pre- and post-search events, measures the search duration, and logs relevant data like query, results, page, and filters using an AnalyticsService. It also demonstrates how to register this subscriber within a Search class.
```php
['onPreSearch', 10],
PostSearchEvent::class => ['onPostSearch', 5],
];
}
public function onPreSearch(PreSearchEvent $event): void
{
$this->startTime = microtime(true);
}
public function onPostSearch(PostSearchEvent $event): void
{
$duration = (microtime(true) - $this->startTime) * 1000;
$this->analytics->track('search_completed', [
'query' => $event->getQuery()->getQueryString(),
'results' => $event->getResultSet()->getTotalResults(),
'page' => $event->getQuery()->getCurrentPage(),
'duration_ms' => round($duration, 2),
'filters' => count($event->getQuery()->getActiveFilters()),
]);
}
}
// Register subscriber in Search class
#[AsSearch('products')]
class ProductSearch extends AbstractSearch
{
public function __construct(private AnalyticsService $analytics)
{
}
public function build(array $options = []): void
{
$this
->addFacet('brand', 'Brand')
->addEventSubscriber(new SearchAnalyticsSubscriber($this->analytics))
;
}
}
```