eigene Filtertypen statt Standard-Facetten
Die Standard-Filter der Layered Navigation reichen bei einfachen Katalogen aus, stoßen aber bei Range-Slidern, mehrstufiger UND/ODER-Logik oder kategoriespezifischen Facetten schnell an ihre Grenzen. Wer die Layered Navigation in Magento 2 wirklich anpassen will, muss FilterList, FilterableAttributeList und die Aggregations-Schicht von OpenSearch verstehen, bevor eigener Code entsteht.
Inhaltsverzeichnis
- 1. Warum Standard-Filter oft nicht ausreichen
- 2. Architektur: Layer, FilterList und Aggregationen
- 3. Einen eigenen Filtertyp implementieren
- 4. Reihenfolge und Sichtbarkeit der Filter steuern
- 5. Swatches korrekt in die Layered Navigation einbinden
- 6. Multi-Select-Filter und UND/ODER-Logik
- 7. Performance: Aggregationen und Facet-Caching
- 8. Frontend-Integration mit Hyvä und Alpine.js
- 9. Filteransätze im direkten Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Warum Standard-Filter oft nicht ausreichen
Die Layered Navigation ist Magentos Facettensystem für Kategorie- und Suchergebnisseiten. Standardmäßig deckt sie Auswahl-Attribute, Preisspannen und die Kategoriehierarchie ab, alle drei aus dem Suchindex per Aggregation berechnet. Für einen Katalog mit wenigen, klar abgegrenzten Attributen reicht das meist aus. Sobald aber Range-Slider für technische Kennwerte, mehrstufige Kombinationen aus UND- und ODER-Logik oder kategoriespezifische Filter gebraucht werden, stößt die Layered Navigation in ihrer Standardform an Grenzen.
Ein typisches Beispiel aus der Praxis: Ein Elektronikshop möchte Prozessorleistung als numerischen Bereich mit Schiebereglern filterbar machen, statt als feste Stufen wie bei einem select-Attribut. Ein Modeshop möchte Größen und Farben kombinieren können, ohne dass eine Auswahl die andere ungültig macht, wenn eine bestimmte Kombination nicht lagernd ist. Beide Fälle erfordern Anpassungen an der Layered Navigation, die über die Backend-Konfiguration von Attributen hinausgehen.
Der Aufwand für diese Anpassungen lohnt sich, weil die Layered Navigation einer der am stärksten frequentierten Bereiche im Storefront ist. Kunden, die über Filter zum passenden Produkt gelangen, haben in der Regel eine höhere Kaufwahrscheinlichkeit als Kunden, die durch lange Produktlisten scrollen. Jede Verbesserung der Filterlogik wirkt sich direkt auf die Conversion-Rate von Kategorieseiten aus.
2. Architektur: Layer, FilterList und Aggregationen
Im Zentrum der Layered Navigation steht die Klasse Magento\Catalog\Model\Layer, die über den LayerResolver je nach Kontext (Kategorie oder Suchergebnis) eine passende Instanz liefert. Die eigentliche Filterlogik wird durch FilterList zusammengesetzt, das für jedes facettierbare Attribut den passenden Filter-Typ instanziiert, etwa Attribute, Price oder Category. Welche Attribute überhaupt berücksichtigt werden, bestimmt FilterableAttributeList, das die im Backend markierten Attribute lädt.
Auf OpenSearch-Ebene wird jeder dieser Filter letztlich zu einer Aggregation in der Suchquery, meist einer terms-Aggregation für Auswahlwerte oder einer range-Aggregation für Preise. Die Ergebniszahlen neben jeder Filteroption (die Trefferanzahl pro Wert) stammen direkt aus diesen Aggregationen, nicht aus einer separaten Datenbankabfrage. Wer die Layered Navigation erweitern will, muss also sowohl die PHP-Ebene (Filter-Klassen) als auch die Query-Ebene (Aggregationen) im Blick behalten, denn beide müssen zueinander passen.
<?php
declare(strict_types=1);
namespace Mironsoft\LayeredNavigation\Model\Layer\Filter;
use Magento\Catalog\Model\Layer\Filter\AbstractFilter;
use Magento\Catalog\Model\Layer\Filter\Item\DataBuilder;
use Magento\Framework\App\RequestInterface;
/**
* Custom range filter for numeric attributes with fixed, business-defined buckets.
*/
final class RangeFilter extends AbstractFilter
{
/**
* Applies the range filter based on the current request and updates the collection.
*
* @param RequestInterface $request Current storefront request
* @return $this
*/
public function apply(RequestInterface $request): static
{
$rangeParam = $request->getParam($this->_requestVar);
if (!$rangeParam) {
return $this;
}
[$from, $to] = array_pad(explode('-', (string) $rangeParam), 2, null);
$this->getLayer()->getProductCollection()->addFieldToFilter(
$this->getAttributeModel()->getAttributeCode(),
['from' => $from, 'to' => $to]
);
$this->getLayer()->getState()->addFilter(
$this->_createItem((string) $rangeParam, $rangeParam)
);
return $this;
}
}
3. Einen eigenen Filtertyp implementieren
Der obige RangeFilter ist ein Ausgangspunkt für einen eigenen Filtertyp innerhalb der Layered Navigation, muss aber noch mit einem passenden Renderer für die Anzeige der Optionen und einer eigenen Aggregation auf OpenSearch-Seite ergänzt werden. Für die Werteberechnung (wie viele Produkte pro Bucket) reicht Magentos generische range-Aggregation aus, solange die Bucket-Grenzen fest definiert sind und nicht dynamisch aus dem Katalog abgeleitet werden müssen.
Die Registrierung eines neuen Filtertyps erfolgt über eine eigene FilterList-Erweiterung per di.xml, die für bestimmte Attributcodes den eigenen Filter statt des Standard-Attribut-Filters instanziiert. Wichtig für eine saubere Layered Navigation-Erweiterung: Der neue Filter muss dieselben Interfaces implementieren wie die Standard-Filter, damit Layout-Templates und Widgets ihn ohne Sonderfälle verarbeiten können.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Catalog\Model\Layer\FilterList">
<arguments>
<argument name="filterableAttributes" xsi:type="array">
<item name="cpu_performance" xsi:type="string">
Mironsoft\LayeredNavigation\Model\Layer\Filter\RangeFilter
</item>
</argument>
</arguments>
</type>
</config>
4. Reihenfolge und Sichtbarkeit der Filter steuern
Die Reihenfolge der Filter in der Layered Navigation wird über die Attributposition im Attribut-Set gesteuert, ist aber in vielen Projekten unzureichend gepflegt, weil sie beim Anlegen neuer Attribute selten aktiv angepasst wird. Für Merchandising-Zwecke lohnt sich eine bewusste Reihenfolge: Filter mit hoher Kaufrelevanz, etwa Preis und Marke, sollten weiter oben stehen als selten genutzte technische Filter.
Für die Sichtbarkeit einzelner Filter in bestimmten Kategorien reicht die Standard-Konfiguration nicht aus, da Magento Facetten global pro Attribut aktiviert, nicht pro Kategorie. Eine gängige Lösung ist ein Plugin auf FilterableAttributeList::getList(), das Attribute anhand der aktuellen Kategorie oder ihres Attribut-Sets herausfiltert, bevor sie überhaupt in die Layered Navigation gelangen. So verschwinden Filter, die für eine bestimmte Produktgruppe keinen Sinn ergeben, automatisch aus der Anzeige.
5. Swatches korrekt in die Layered Navigation einbinden
Swatches (visuelle Filter für Farbe und Muster) sind technisch eine Sonderform von Auswahl-Attributen und werden von der Layered Navigation über eine eigene Renderer-Klasse dargestellt, die zusätzlich zur Textoption ein Bild oder einen Farbwert lädt. Ein häufiger Fehler bei individuellen Swatch-Anpassungen: Das visuelle Rendering wird verändert, aber die zugrunde liegende Aggregation bleibt unangetastet, sodass die Trefferzahlen neben jedem Swatch nicht mit der tatsächlichen visuellen Darstellung übereinstimmen.
Bei Multi-Swatch-Attributen, etwa Farbverläufe mit mehreren Farbwerten pro Produkt, lohnt sich zusätzlich ein Blick auf die Aggregationsgröße (size-Parameter der terms-Aggregation). Ein zu niedriger Wert schneidet seltenere Farboptionen aus der Layered Navigation heraus, ohne dass das im Frontend sofort auffällt, was in Katalogen mit vielen Farbvarianten zu einer unvollständigen Filterliste führt.
6. Multi-Select-Filter und UND/ODER-Logik
Innerhalb eines Attributs kombiniert Magentos Layered Navigation standardmäßig mehrere ausgewählte Werte mit ODER-Logik (ein Produkt muss mindestens einen der ausgewählten Werte haben), während unterschiedliche Attribute mit UND-Logik kombiniert werden (ein Produkt muss alle gewählten Filter gleichzeitig erfüllen). Dieses Verhalten ist für die meisten Kataloge sinnvoll, in Nischenfällen, etwa wenn Größe und Farbe gemeinsam eine verfügbare Lager-Kombination beschreiben müssen, reicht es nicht aus.
{
"query": {
"bool": {
"filter": [
{ "terms": { "color": ["red", "blue"] } },
{ "terms": { "size": ["m", "l"] } }
]
}
},
"aggs": {
"available_combinations": {
"terms": { "field": "color_size_combination", "size": 50 }
}
}
}
Für echte Kombinationslogik in der Layered Navigation hilft ein zusätzliches, zur Indexierungszeit berechnetes Kombinationsfeld (im Beispiel color_size_combination), das jede tatsächlich lagernde Farbe-Größe-Kombination als eigenen Wert abbildet. Dieser Ansatz vermeidet komplizierte Nested-Queries zur Laufzeit und verlagert die Komplexität in den Indexierungsprozess, was in der Regel die bessere Performance-Bilanz ergibt.
7. Performance: Aggregationen und Facet-Caching
Jede zusätzliche Aggregation in der Layered Navigation erhöht die Antwortzeit der Suchquery, weil OpenSearch für jede Aggregation zusätzliche Berechnungen über die gefilterte Trefferliste durchführen muss. Bei Katalogen mit vielen facettierbaren Attributen kann die Summe dieser Aggregationen spürbar zur Ladezeit der Kategorieseite beitragen, besonders bei hoher Parallelität im Storefront.
Ein bewährter Hebel ist, Aggregationsgrößen bewusst zu begrenzen (size-Parameter) und Attribute, die selten als Filter genutzt werden, aus der Layered Navigation zu entfernen, statt sie ungenutzt mitzuberechnen. Zusätzlich lässt sich der Full Page Cache für Kategorieseiten ohne aktive Filterauswahl nutzen, während gefilterte Ansichten (mit Query-Parametern) typischerweise ungecacht bleiben, da die Kombinationsvielfalt zu groß für einen sinnvollen Cache-Hit-Rate wäre.
8. Frontend-Integration mit Hyvä und Alpine.js
Im Hyvä-Theme wird die Layered Navigation typischerweise über eine ViewModel-Klasse an das Template gereicht, die Filterdaten in ein für Alpine.js konsumierbares Format bringt. Für individuelle Filtertypen wie den Range-Slider aus Abschnitt 3 lohnt sich eine eigene Alpine.js-Komponente, die den Sliderwert lokal hält und erst bei Loslassen des Reglers per Formular-Submit an den Server sendet, statt bei jeder Bewegung eine neue Anfrage auszulösen.
<div x-data="rangeFilter({min: 0, max: 1000, current: [100, 600]})">
<input type="range" x-model.number="current[0]" :max="max" @change="submit()">
<input type="range" x-model.number="current[1]" :max="max" @change="submit()">
<span x-text="`${current[0]} - ${current[1]}`"></span>
</div>
<script>
function rangeFilter(config) {
return {
min: config.min,
max: config.max,
current: config.current,
submit() {
// Debounced form submit, avoids one request per pixel of slider movement
window.location.href = `?cpu_performance=${this.current[0]}-${this.current[1]}`;
}
};
}
</script>
9. Filteransätze im direkten Vergleich
Die folgende Tabelle vergleicht Standard-Filter mit individuellen Erweiterungen der Layered Navigation.
| Anforderung | Standard-Filter | Individuelle Erweiterung | Nutzen |
|---|---|---|---|
| Numerische Bereiche | Feste select-Stufen | Range-Filter mit Slider | Feinere, intuitivere Auswahl |
| Farbe + Größe kombinieren | Unabhängige UND-Logik | Kombinationsfeld im Index | Nur lagernde Kombinationen sichtbar |
| Kategoriespezifische Filter | Global pro Attribut aktiv | Plugin auf FilterableAttributeList | Relevante Filter je Kategorie |
| Viele Facetten gleichzeitig | Alle Aggregationen berechnet | Bewusste size-Begrenzung | Kürzere Antwortzeiten |
Wo Standard-Filter an ihre Grenzen stoßen, lohnt sich meist eine gezielte, punktuelle Erweiterung der Layered Navigation statt einer kompletten Eigenentwicklung des Facettensystems.
Mironsoft
Magento-Suche, Layered Navigation und Hyvä-Frontend-Entwicklung
Filter, die eure Kunden wirklich zum Produkt führen?
Wir bauen individuelle Filtertypen, Kombinationslogik und Hyvä-taugliche Alpine.js-Komponenten für eure Layered Navigation, mit Fokus auf Performance und Merchandising-Wirkung.
Custom Filter
Range-Filter, Kombinationslogik und kategoriespezifische Facetten
Hyvä-Integration
Alpine.js-Komponenten für Slider, Swatches und Multi-Select
Performance-Tuning
Aggregationsgrößen und Cache-Strategie für schnelle Kategorieseiten
10. Zusammenfassung
Die Layered Navigation in Magento 2 lässt sich weit über die Standard-Konfiguration hinaus anpassen, sobald man Layer, FilterList und die zugrunde liegenden OpenSearch-Aggregationen als zusammenhängendes System versteht. Eigene Filtertypen wie Range-Slider, kategoriespezifische Sichtbarkeit und echte Kombinationslogik für Farbe und Größe lösen konkrete Merchandising-Probleme, die mit Standard-Attributfiltern nicht abbildbar sind.
Performance bleibt dabei der limitierende Faktor: Jede zusätzliche Aggregation kostet Antwortzeit, weshalb Aggregationsgrößen bewusst begrenzt und selten genutzte Filter aus der Layered Navigation entfernt werden sollten. In Kombination mit einer sauberen Hyvä-Integration über Alpine.js entsteht eine Filterlogik, die sowohl technisch performant als auch für Kunden intuitiv bedienbar ist.
Layered Navigation in Magento 2 — Das Wichtigste auf einen Blick
Architektur verstehen
Layer, FilterList und FilterableAttributeList arbeiten zusammen, Aggregationen liefern die Trefferzahlen.
Eigene Filtertypen
Range-Slider und Kombinationslogik über eigene Filter-Klassen plus passende Aggregation.
Kategoriespezifische Filter
Per Plugin auf FilterableAttributeList, da Magento Facetten sonst nur global steuert.
Performance zuerst
Aggregationsgrößen begrenzen, ungenutzte Filter entfernen, Full Page Cache gezielt einsetzen.