Zum Inhalt springen

Interna des Ersetzungsflusses

Diese Seite dokumentiert das Laufzeitverhalten für Leser, die das Plugin verstehen oder erweitern möchten. Nichts davon ist nötig, um es zu konfigurieren.

Das Plugin hört auf drei Storefront-Page-Loaded-Events und zwei Entity-Loaded-Events:

EventHandlerSeitenart
ProductPageLoadedEventonProductPageLoadedProduktdetail
NavigationPageLoadedEventonNavigationPageLoadedKategorie / Listing
LandingPageLoadedEventonLandingPageLoadedLanding-Seite
CategoryEvents::CATEGORY_LOADED_EVENTonCategoryEntityLoadedCMS-Navigation-Widget (XHR)
LandingPageEvents::LANDING_PAGE_LOADED_EVENTonLandingPageEntityLoadedLanding-Seiten-Entity-Load

Alle fünf liegen in Subscriber\PageLoadedSubscriber.

Die Page-Loaded-Handler decken normale Storefront-Navigation ab. Die beiden Entity-Loaded-Handler decken den CMS-Navigation-Widget-XHR ab (den Aufruf, mit dem Ihre Storefront Kategorie-Inhalte lazy nachlädt) sowie direkte Landing-Seiten-Entity-Loads, sodass sich die Ersetzung konsistent verhält, egal ob das Layout als Teil der Hauptseite oder als Teil eines Widget-XHRs gerendert wird.

Für jeden relevanten Request macht der Handler grob:

salesChannelId = aus dem Request auflösen
1. Hat das Basislayout das Ersetzungsmodus-Tor passiert?
- Produktseite → ProductPageReplaceModeEnum (4 Werte)
- Kategorienseite → NavigationPageReplaceModeEnum (3 Werte)
- Landing-Seite → LandingPageReplaceModeEnum (3 Werte)
Falls nein → return.
2. alternativeTypes = SettingsService::getAlternativeReplaceTypes(baseType, salesChannelId)
Falls leer → return.
3. Jede p2lab_cms_assigner_cms_page_to_rule durchlaufen, deren cmsPage.type IN alternativeTypes liegt:
- Sortiert nach rule.priority ASC (Shopwares Standard-Sortierung).
- In Seiten von 100 (LIMIT_PER_REQUEST), bis zu 500 insgesamt (LIMIT_TOTAL).
- Zeilen nach cmsPageId gruppieren — einem Layout können mehrere Regeln angehängt sein.
4. Für jede cmsPageId RuleConditionService::match(rules, page, salesChannelContext) aufrufen:
- Alle angehängten Regeln müssen passen (UND-Semantik).
- Eine Regel wird aus ihren Bedingungen gebaut, gefiltert auf ConditionNameEnum::CONDITIONS und CONTAINERS.
- Der Matcher verzweigt nach Seitentyp → ProductRuleScope / CategoryRuleScope / LandingRuleScope.
5. Erster Treffer gewinnt. Das Plugin lädt die CmsPage über SalesChannelCmsPageLoaderInterface
mit einem um Produkt / Kategorie / landingPage gebauten EntityResolverContext
und weist sie dann dem Page-Objekt zu.

Das Plugin speichert Regel-zu-Layout-Zuweisungen in einer einzelnen Junction-Tabelle. Jede Zeile enthält:

  • id — Primärschlüssel.
  • cms_page_id + cms_page_version_id — das Alternativlayout.
  • rule_id — die Regel, von der es abhängt.

Ein Layout mit N angehängten Regeln hat N Zeilen; eine Regel, die M Layouts angehängt ist, hat M Zeilen. In beide Richtungen lesbar.

Das Plugin definiert drei eigene Rule Scopes (unter Rule\Scope\), damit seine eigenen Bedingungen (und die Standard-Shopware-Bedingungen) introspektieren können, was betrachtet wird:

  • ProductRuleScope — hält das SalesChannelProductEntity plus den Sales-Channel-Kontext. Wird von Produktseiten-Handlern verwendet und von InCategoryRule / InCategoryWithChildrenRule geteilt, wenn sie gegen die categoryIds eines Produkts auswerten.
  • CategoryRuleScope — hält die categoryId plus den Sales-Channel-Kontext. Exposed isHomepage() (verwendet von IsHomepageRule).
  • LandingRuleScope — hält die LandingPage-Page plus den Sales-Channel-Kontext.

Alle drei erben von AbstractRuleScope, das den SalesChannelContext und das Kategorie-Repository hält (damit Regeln, die den Baum durchlaufen, suchen können).

Das Plugin bringt drei eigene Rule-Klassen mit (unter Rule\):

  • IsHomepageRule — boolesche Prüfung gegen CategoryRuleScope::isHomepage(). Gibt auf Nicht-Kategorie-Scopes false zurück.
  • InCategoryRule — UUID-Vergleich gegen die categoryIds des Produkts oder die eigene id der Kategorie.
  • InCategoryWithChildrenRule erweitert InCategoryRule — durchläuft zusätzlich den Kategorien-path (Shopware speichert die Vorfahrenkette als |id1|id2|id3|), um Nachkommen zu matchen.

Es gibt auch Rule\Container\MatchAllRule — einen Stand-in für Shopwares “Alle-Positionen-Container”, dessen produkt-only-Semantik im Kontext einer CMS-Layout-Auswahl keinen Sinn ergibt. Das Plugin ersetzt sie durch eine eigene Implementierung, die nur dann true zurückgibt, wenn jede Kindregel gegen den aktuellen Scope passt.

ConditionNameEnum definiert zwei Arrays: CONTAINERS und CONDITIONS. Alles, was nicht in diesen Arrays ist, wird beim Bauen einer Regel stillschweigend ignoriert. Die Ausschlussliste ist absichtlich breit — viele Standard-Shopware-Bedingungen (Warenkorbsummen, Line-Item-Attribute, Versandart) zielen auf den Checkout-Flow und haben beim Auswählen eines Layouts für eine noch nicht aufgebaute Seite keinen Sinn.

Was genau unterstützt wird, siehe Eigene Regelbedingungen.

onCategoryEntityLoaded ist am subtilsten. Er läuft auf category.loaded-Events und agiert nur, wenn der Request wie ein CMS-Navigation-Widget-XHR aussieht — der Pfad matcht ^/widgets/cms/navigation/<32-hex>$ und der Query-String trägt navigationId + cms-page-id. So hält das Plugin die Ersetzung konsistent, wenn die Storefront den Inhalt einer Kategorie über den Widget-XHR holt (zusätzlich zum initialen Page-Render).

Er macht dieselbe Modus-Prüfung wie der Haupt-onNavigationPageLoaded-Handler, bevor er die cmsPageId der Entity überschreibt.

onLandingPageEntityLoaded läuft auf landing_page.loaded-Events und wendet das Standard-Landing-Seiten-Layout an, wenn die Landing-Seite kein eigenes Layout hat. Dies ist der Einstiegspunkt für die Default-Layout-Einstellung der Karte Landing-Seite.

  • Es modifiziert nicht die CMS-Seite selbst — nur welche CmsPageEntity gerendert wird.
  • Es berührt nicht die Kategorie- / Produkt- / Landing-Seiten-Entity in der Datenbank — die Überschreibung passiert im Speicher, pro Request.
  • Es cached seine Entscheidungen nicht; jeder Request wertet die Regeln neu aus. Die Repository-Suche trifft die Standard-Caches von Shopware, aber die Regel-Auswertung ist pro Request.