von indexer.xml bis zum vollstaendigen Mview Zyklus
Wer eigene Daten performant durchsuchbar oder aggregierbar machen will, kommt an einem eigenen Indexer nicht vorbei. Statt teure Berechnungen bei jedem Frontend Request neu auszufuehren, verlagert ein eigener Indexer die Arbeit in einen kontrollierten, wiederholbaren Prozess, der genau dann laeuft, wenn sich die zugrunde liegenden Daten aendern.
Inhaltsverzeichnis
- 1. Wann sich ein eigener Indexer wirklich lohnt
- 2. IndexerInterface und ActionInterface im Ueberblick
- 3. Deklaration in indexer.xml
- 4. Die Action Klasse: execute, executeFull, executeList, executeRow
- 5. Eigene Index-Tabelle sauber entwerfen
- 6. Mview anbinden: Changelog-Tabellen verstehen
- 7. Registrierung ueber di.xml und Dependency Injection
- 8. CLI Integration, Debugging und Tests
- 9. Eigener Indexer im Vergleich zu Alternativen
- 10. Zusammenfassung
- 11. FAQ
1. Wann sich ein eigener Indexer wirklich lohnt
Ein eigener Indexer in Magento 2 ist immer dann sinnvoll, wenn eine Berechnung wiederholt gebraucht wird, aber selten neu berechnet werden muss. Klassisches Beispiel: ein aggregierter Lagerbestand ueber mehrere externe Lager, eine berechnete Beliebtheits-Kennzahl fuer Produkte, oder eine denormalisierte Preistabelle fuer ein individuelles B2B Preismodell. Ohne Indexer wuerde diese Berechnung bei jedem Seitenaufruf neu laufen, was Antwortzeiten unnoetig verlaengert und die Datenbank unter Last setzt.
Magento selbst zeigt mit den eingebauten Indexern wie catalog_product_price oder catalogsearch_fulltext das Muster, das ein eigener Indexer uebernehmen sollte: Rohdaten liegen in normalisierten Tabellen, ein Indexer verdichtet sie zu einer schnell lesbaren Zieltabelle, und diese Zieltabelle wird im Frontend gelesen statt der Rohdaten. Der Indexer selbst laeuft entweder per Cron im Hintergrund oder synchron beim Speichern, je nach gewaehltem Modus.
Bevor man einen eigenen Indexer baut, lohnt sich die Frage, ob nicht ein einfacherer Mechanismus reicht. Kleine, selten geaenderte Daten kann ein Cache-Tag im Full Page Cache genauso gut abbilden. Ein Indexer lohnt sich erst, wenn die Berechnung selbst teuer ist, wenn viele Datensaetze betroffen sind, oder wenn ein inkrementeller Reindex bei einzelnen Aenderungen (Mview) einen echten Performance-Vorteil gegenueber vollstaendiger Neuberechnung bringt.
2. IndexerInterface und ActionInterface im Ueberblick
Ein eigener Indexer besteht in Magento 2 aus mehreren zusammenspielenden Teilen. Das Magento\Framework\Indexer\IndexerInterface ist die aeussere Fassade, die von bin/magento indexer:reindex und dem Admin Grid angesprochen wird. Die eigentliche Logik steckt aber nicht in dieser Schnittstelle, sondern in einer separaten Action Klasse, die das Magento\Framework\Indexer\ActionInterface implementiert. Diese Trennung erlaubt es, dieselbe Indexer Logik sowohl fuer vollstaendige als auch fuer partielle Reindex Laeufe wiederzuverwenden.
Zusaetzlich kommt bei inkrementellem Reindex das Magento\Framework\Mview\ActionInterface ins Spiel, das ueber execute(array $ids) nur die betroffenen Entity IDs verarbeitet. Ein eigener Indexer muss also mindestens zwei Interfaces bedienen: eines fuer den kompletten Lauf ueber alle Datensaetze und eines fuer den gezielten Lauf ueber eine ID Liste. Magento generiert daraus automatisch die noetigen Indexer Tabellen und Views, sobald die Deklaration in indexer.xml korrekt ist.
Wichtig ist das Verstaendnis, dass ein eigener Indexer in Magento kein einzelnes PHP Objekt ist, sondern ein Zusammenspiel aus Deklaration, Datenhaltung und Ausfuehrungslogik. Wer nur die Action Klasse schreibt, aber die Deklaration vergisst, bekommt einen Indexer, der weder in der CLI noch im Admin Grid sichtbar ist. Die folgenden Abschnitte bauen diese Teile in der Reihenfolge auf, in der sie beim Entwickeln eines eigenen Indexers tatsaechlich gebraucht werden.
3. Deklaration in indexer.xml
Jeder eigene Indexer beginnt mit einer Deklaration in etc/indexer.xml. Dort wird ein eindeutiger Indexer Code vergeben, die Action Klasse referenziert, ein View fuer den Mview Mechanismus verknuepft und ein sprechender Titel fuer das Admin Grid gesetzt. Der Indexer Code ist der zentrale Identifier, unter dem der Indexer spaeter per CLI angesprochen wird, etwa bin/magento indexer:reindex vendor_pricematrix.
Der indexer Knoten unterstuetzt zusaetzlich ein class Attribut fuer die konkrete Indexer Implementierung sowie optionale Kindknoten fuer title, description und fieldset. Fuer Datenaustausch mit anderen Indexern lassen sich ueber <dependencies> Abhaengigkeiten definieren, sodass ein eigener Indexer zum Beispiel erst nach catalog_product_price laufen kann, wenn er auf dessen Ergebnissen aufbaut.
<?xml version="1.0"?>
<!-- app/code/Vendor/PriceMatrix/etc/indexer.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Indexer/etc/indexer.xsd">
<indexer id="vendor_pricematrix"
view_id="vendor_pricematrix"
class="Vendor\PriceMatrix\Model\Indexer\PriceMatrixIndexer">
<title translate="true">B2B Preismatrix</title>
<description translate="true">Aggregiert Kundengruppen-Preise in eine flache Tabelle</description>
<!-- Runs only after the core price indexer has finished -->
<dependencies>
<indexer id="catalog_product_price"/>
</dependencies>
</indexer>
</config>
Parallel dazu wird in etc/mview.xml der View mit derselben view_id definiert, inklusive der Tabellen, deren Aenderungen ueberwacht werden sollen, und der Class Table Observer die daraus Aenderungs IDs ableiten. Ohne diesen View bleibt ein eigener Indexer auf vollstaendige Reindex Laeufe beschraenkt und kann nicht inkrementell aktualisiert werden, auch wenn der Indexer Modus im Admin Grid auf "Bei Speichern aktualisieren" steht.
4. Die Action Klasse: execute, executeFull, executeList, executeRow
Die Action Klasse eines eigenen Indexers implementiert IndexerActionInterface mit vier Methoden: executeFull() fuer den kompletten Neuaufbau, executeList(array $ids) fuer eine Liste betroffener IDs, executeRow($id) fuer einen einzelnen Datensatz und execute($ids) als generische Variante, die intern meist an executeList delegiert. Diese Aufteilung erlaubt es Magento, je nach Trigger die passende Methode aufzurufen: ein manueller indexer:reindex ruft executeFull, ein Speichervorgang im Admin ruft executeRow.
In der Praxis empfiehlt es sich, die eigentliche Datenverarbeitung in eine separate, injizierbare Klasse auszulagern und die Action Klasse selbst schlank zu halten. So kann dieselbe Verarbeitungslogik sowohl von der Action Klasse als auch von einem eigenen CLI Command oder einem Consumer aus der Message Queue wiederverwendet werden. Das reduziert Duplikate und macht den eigenen Indexer leichter testbar, weil die reine Berechnungslogik ohne Framework Abhaengigkeiten unit getestet werden kann.
<?php
declare(strict_types=1);
namespace Vendor\PriceMatrix\Model\Indexer;
use Magento\Framework\Indexer\ActionInterface;
use Magento\Framework\Mview\ActionInterface as MviewActionInterface;
use Vendor\PriceMatrix\Model\ResourceModel\PriceMatrix\PriceCalculator;
/**
* Custom indexer action building the flattened B2B price matrix table.
*/
class PriceMatrixIndexer implements ActionInterface, MviewActionInterface
{
/**
* @param PriceCalculator $priceCalculator Handles the actual price aggregation.
*/
public function __construct(
private readonly PriceCalculator $priceCalculator
) {
}
/**
* Full reindex, triggered by bin/magento indexer:reindex.
*
* @return void
*/
public function executeFull(): void
{
$this->priceCalculator->rebuildAll();
}
/**
* Partial reindex for a list of product IDs (Mview or CLI --id).
*
* @param int[] $ids
* @return void
*/
public function executeList(array $ids): void
{
$this->priceCalculator->rebuildForIds($ids);
}
/**
* Partial reindex for a single product ID, used when saving in Admin.
*
* @param int $id
* @return void
*/
public function executeRow($id): void
{
$this->priceCalculator->rebuildForIds([(int) $id]);
}
/**
* Generic entry point used by the Mview changelog processor.
*
* @param int[] $ids
* @return void
*/
public function execute($ids): void
{
$this->executeList((array) $ids);
}
}
5. Eigene Index-Tabelle sauber entwerfen
Die Zieltabelle eines eigenen Indexers sollte immer flach und lesefreundlich sein, ohne Joins zur Laufzeit. Das bedeutet in der Praxis: alle Werte, die im Frontend gebraucht werden, liegen bereits denormalisiert in einer Zeile, indiziert auf die Spalten, nach denen tatsaechlich gefiltert wird. Fuer die B2B Preismatrix waere das eine Tabelle mit Produkt ID, Kundengruppe und berechnetem Preis als Primaerschluessel Kombination, ergaenzt um einen Index auf die haeufigste Abfragerichtung.
Ein haeufiger Fehler bei einem eigenen Indexer ist, die Zieltabelle waehrend eines Full Reindex direkt zu leeren und neu zu befuellen. Bei grossen Datenmengen fuehrt das zu einem Zeitfenster, in dem die Tabelle leer oder unvollstaendig ist und das Frontend falsche Ergebnisse liefert. Der etablierte Weg ist eine Replace Tabelle: es wird in eine temporaere Tabelle geschrieben, und erst am Ende per atomarem RENAME TABLE gegen die produktive Tabelle getauscht, genau wie es Magentos eigene Indexer intern handhaben.
| Modus | Ausloeser | Methode | Typischer Einsatz |
|---|---|---|---|
| Update on Save | Admin Speichern | executeRow |
Kleiner Datenbestand, sofortige Konsistenz noetig |
| Update by Schedule | Cron via Mview | executeList |
Grosser Katalog, Batchverarbeitung erwuenscht |
| Full Reindex | indexer:reindex |
executeFull |
Initiale Befuellung, Deploy, Datenreparatur |
| Save deferred | Nach Massenimport | executeList |
Import Pipelines mit gebuendelten IDs |
6. Mview anbinden: Changelog-Tabellen verstehen
Mview steht fuer "Materialized View" und ist der Mechanismus, mit dem Magento Aenderungen an Quelltabellen in Changelog Tabellen protokolliert, um sie spaeter inkrementell zu verarbeiten. Sobald ein eigener Indexer in mview.xml registriert ist, legt Magento automatisch eine Tabelle vendor_pricematrix_cl an und haengt Trigger an die ueberwachten Quelltabellen. Jede INSERT, UPDATE oder DELETE Operation auf diesen Tabellen schreibt die betroffene Entity ID in die Changelog Tabelle.
Der eigentliche Reindex Lauf liest dann diese Changelog Tabelle, sammelt die IDs seit dem letzten Versionsstand und ruft executeList mit genau diesen IDs auf. Das ist der Kern des Partial Reindex und der Grund, warum ein eigener Indexer mit korrekt konfiguriertem Mview deutlich schneller reagiert als ein vollstaendiger Neuaufbau. Wichtig ist, alle relevanten Quelltabellen im View zu erfassen, sonst werden Aenderungen an ihnen schlicht nicht erkannt.
<?xml version="1.0"?>
<!-- app/code/Vendor/PriceMatrix/etc/mview.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Mview/etc/mview.xsd">
<view id="vendor_pricematrix" class="Vendor\PriceMatrix\Model\Indexer\PriceMatrixIndexer" group="indexer">
<subscriptions>
<table name="catalog_product_entity" entity_column="entity_id"/>
<table name="customer_group" entity_column="customer_group_id"/>
</subscriptions>
</view>
</config>
7. Registrierung ueber di.xml und Dependency Injection
Neben indexer.xml und mview.xml braucht ein eigener Indexer in der Regel Eintraege in etc/di.xml, um die Zieltabelle als Resource zu registrieren und den Indexer optional an bestehende Observer zu haengen, etwa um bei jedem Produkt Speichern eine gezielte Invalidierung auszuloesen. Das Framework bringt dafuer bereits einen Standard Observer Magento\Indexer\Observer\AbstractInvalidateIndexerObserver, der sich mit wenigen Zeilen wiederverwenden laesst.
Ein weiterer wichtiger Baustein ist die Registrierung des Indexers als Consumer fuer Message Queue basierte asynchrone Verarbeitung, falls die Berechnung so aufwendig ist, dass sie nicht mehr synchron im Cron Prozess laufen soll. Fuer die meisten eigenen Indexer reicht jedoch der Standard Mechanismus aus Cron und Mview vollstaendig aus, eine zusaetzliche Message Queue Ebene lohnt sich erst bei sehr hohem Datenvolumen oder externen API Abhaengigkeiten in der Berechnung.
8. CLI Integration, Debugging und Tests
Sobald Deklaration und Action Klasse stehen, erscheint der eigene Indexer automatisch in bin/magento indexer:info und kann einzeln mit bin/magento indexer:reindex vendor_pricematrix angestossen werden. Der Modus laesst sich mit indexer:set-mode schedule vendor_pricematrix oder realtime umschalten. Fuer die Fehlersuche ist indexer:status der erste Anlaufpunkt, um zu sehen, ob ein Indexer als "invalid" markiert ist und einen Reindex braucht.
Beim Debugging eines eigenen Indexer Laufs hilft es, die Changelog Tabelle direkt zu inspizieren, um zu pruefen, ob Trigger tatsaechlich Eintraege erzeugen. Fehlt ein Eintrag nach einer erwarteten Aenderung, liegt meist ein vergessenes <table> im mview.xml vor. Unit Tests fuer die Action Klasse sollten die Berechnungslogik isoliert von der Datenbank testen, waehrend Integration Tests mit echten Fixtures pruefen, ob ein executeFull Lauf tatsaechlich die korrekten Zeilen in der Zieltabelle erzeugt.
# Show all registered indexers, including the custom one
bin/magento indexer:info
# Full reindex for a single custom indexer
bin/magento indexer:reindex vendor_pricematrix
# Switch to scheduled (cron-driven) mode
bin/magento indexer:set-mode schedule vendor_pricematrix
# Check current status: valid, invalid, working
bin/magento indexer:status vendor_pricematrix
# Inspect the changelog table directly for debugging
bin/mysql -e "SELECT * FROM vendor_pricematrix_cl ORDER BY version_id DESC LIMIT 20;"
9. Eigener Indexer im Vergleich zu Alternativen
Nicht jede wiederkehrende Berechnung braucht sofort einen eigenen Indexer. Je nach Datenmenge, Aktualitaetsanforderung und Komplexitaet gibt es Alternativen, die weniger Implementierungsaufwand bedeuten, aber auch weniger Kontrolle ueber den Invalidierungszeitpunkt bieten.
| Ansatz | Aktualitaet | Implementierungsaufwand | Wann sinnvoll |
|---|---|---|---|
| Eigener Indexer | Kontrolliert, inkrementell | Hoch | Grosse Datenmengen, teure Berechnung, klare Invalidierungslogik |
| Cron Job mit eigener Tabelle | Fixe Intervalle | Mittel | Verzoegerung von Minuten akzeptabel, keine Mview Trigger noetig |
| Cache Tag im FPC | Bei jedem Request neu berechnet | Niedrig | Kleine, guenstige Berechnungen ohne Datenbankzugriff |
| Observer + Save Event | Sofort bei Aenderung | Niedrig bis mittel | Einzelne Entitaeten, keine Batchverarbeitung noetig |
Der entscheidende Vorteil eines eigenen Indexers gegenueber einem simplen Cron Job ist die Anbindung an Mview: Aenderungen werden ereignisbasiert erfasst, statt in festen Intervallen zu pruefen, ob sich ueberhaupt etwas geaendert hat. Das reduziert unnoetige Reindex Laeufe erheblich, insbesondere bei Katalogen, in denen sich nur ein kleiner Teil der Produkte pro Tag aendert. Ein eigener Indexer ist damit fast immer die richtige Wahl, sobald die Datenmenge oder die Berechnungskosten ein einfaches "auf jeder Anfrage neu rechnen" ausschliessen.
Mironsoft
Magento 2 Architektur, Indexer und Performance Engineering
Braucht euer Datenmodell einen eigenen Indexer?
Wir entwerfen, implementieren und testen massgeschneiderte Indexer fuer Magento 2, inklusive Mview Anbindung, Tabellendesign und Monitoring, damit teure Berechnungen nie mehr im Frontend passieren.
Indexer Design
Tabellenstruktur, Invalidierungslogik und Abhaengigkeiten sauber planen
Implementierung
indexer.xml, Action Klassen und Mview Konfiguration produktionsreif umsetzen
Monitoring
Reindex Laufzeiten, Fehlerraten und Invalidierungen dauerhaft im Blick
10. Zusammenfassung
Ein eigener Indexer in Magento 2 besteht aus einer klaren Deklaration in indexer.xml, einer Action Klasse mit den vier Methoden executeFull, executeList, executeRow und execute, einer flachen Zieltabelle mit atomarem Austausch per RENAME TABLE, sowie einer Mview Registrierung fuer inkrementellen Reindex. Wer diese Bausteine sauber trennt, bekommt einen Indexer, der sich nahtlos in die vorhandene CLI, das Admin Grid und den Cron Betrieb einfuegt.
Der groesste Fehler beim Bau eines eigenen Indexers ist, Mview zu ignorieren und stattdessen ausschliesslich auf vollstaendige Reindex Laeufe zu setzen. Das funktioniert bei kleinen Katalogen, skaliert aber nicht, sobald die Datenmenge waechst. Eine saubere Trennung zwischen Berechnungslogik und Framework Integration macht den eigenen Indexer ausserdem testbar und langfristig wartbar, auch wenn sich die zugrunde liegenden Geschaeftsregeln aendern.
Eigene Indexer in Magento 2 schreiben, das Wichtigste auf einen Blick
Deklaration
indexer.xml mit eindeutigem Code, Action Klasse und optionalen Abhaengigkeiten zu anderen Indexern.
Action Klasse
executeFull, executeList und executeRow trennen vollstaendigen und partiellen Reindex sauber.
Mview
Changelog Tabellen erfassen Aenderungen ereignisbasiert und ermoeglichen echten Partial Reindex.
Tabellendesign
Flache Zieltabelle, atomarer Austausch per RENAME TABLE, keine Joins zur Laufzeit.