Skip to content

Príklad: vlastná výsledková stránka

Výsledková stránka s facetmi, radením a stránkovaním nad Search API. Volanie prebieha zo servera, takže verejný kľúč nemusí opustiť backend a odpoveď sa dá cachovať.

Backend

php
final class SearchController
{
    private const PER_PAGE = 24;

    public function __construct(private readonly MettyClient $metty) {}

    public function __invoke(Request $request): Response
    {
        $query = SearchQuery::for($request->query->get('q', ''))
            ->perPage(self::PER_PAGE)
            ->page(max(1, $request->query->getInt('page', 1)))
            ->withSections('facets', 'categories', 'suggestions');

        if ($sort = $request->query->get('sort')) {
            $query = $query->sortBy($sort);
        }

        if ($category = $request->query->get('category')) {
            $query = $query->category($category);
        }

        foreach ($request->query->all('filter') as $field => $values) {
            foreach ((array) $values as $value) {
                $query = $query->facet($field, $value);
            }
        }

        try {
            $response = $this->metty->search()->search($query);
        } catch (ConfigurationException) {
            return new RedirectResponse($this->urlFor(['q' => $request->query->get('q', '')]));
        }

        return $this->render('search.html.twig', ['results' => $response]);
    }
}

ConfigurationException zachytávame preto, že požiadavka za hranicou 200 výsledkov je neplatná. Namiesto chyby je používateľsky prijateľnejšie vrátiť sa na prvú stranu.

Zvýraznenie zhody

Zhodu označuje server hranatými zátvorkami v poli highlight. Na klientovi ich stačí nahradiť značkou a nič nedopočítavať:

php
function highlight(?string $marked, string $fallback): string
{
    if ($marked === null) {
        return htmlspecialchars($fallback);
    }

    $escaped = htmlspecialchars($marked);

    return str_replace(['[', ']'], ['<mark>', '</mark>'], $escaped);
}
twig
<h3>{{ highlight(product.highlight.name ?? null, product.name)|raw }}</h3>

Escapovanie musí prebehnúť pred nahradením zátvoriek, inak by sa do stránky dostalo HTML z názvu produktu.

Facety a filtre

facets je zoznam polí a každé nesie strojový názov aj názov na zobrazenie:

twig
{% for facet in results.facets %}
  <fieldset>
    <legend>{{ facet.label }}</legend>
    {% for value in facet.values %}
      <label>
        <input type="checkbox" name="filter[{{ facet.field }}][]" value="{{ value.value }}"
               {{ value.value in active[facet.field]|default([]) ? 'checked' }}>
        {{ value.value }} <span>({{ value.count }})</span>
      </label>
    {% endfor %}
  </fieldset>
{% endfor %}

Viac hodnôt jedného poľa znamená ALEBO, hodnoty rôznych polí sa kombinujú cez A ZÁROVEŇ. Počty v odpovedi sú prepočítané podľa aktívnych filtrov, takže sa dajú zobraziť priamo.

Stránkovanie

php
$lastPage = min(
    $response->pages,
    intdiv(SearchQuery::MAX_WINDOW, $response->perPage),
);

Ranguje sa prvých 200 výsledkov, preto je posledná dostupná strana daná touto hranicou, nie celkovým počtom nájdených produktov. Ak potrebujete zákazníkovi ponúknuť viac, zúžte výber kategóriou alebo facetom — hlbšie stránkovanie by aj tak vrátilo neusporiadané poradie.

Volanie priamo z prehliadača

Verejný kľúč je určený na čítanie, takže Search API sa dá volať aj z frontendu:

js
const params = new URLSearchParams({
    key: PUBLIC_API_KEY,
    q: query,
    per_page: '24',
    include: 'facets,categories'
})

for (const [field, values] of Object.entries(filters)) {
    for (const value of values) {
        params.append(`${field}[]`, value)
    }
}

const response = await fetch(`https://search.api.metty.eu/search?${params}`)

if (!response.ok) {
    const { error } = await response.json()
    throw new Error(error)
}

const { products, total, facets } = await response.json()

Opakovanú hodnotu posielajte so zátvorkami (farba[]=modrá&farba[]=čierna). Bez nich si server ponechá iba poslednú hodnotu.

Ak vyhľadávanie voláte pri každom stlačenom znaku, použite radšej našepkávanie — má vyšší rate limit a menšiu odpoveď.

Dokumentácia Metty