Textbausteine: Technische Details
Textbausteine für Shopware - Ablauf der Ersetzung, Caching, Datenmodell und Plugin-Struktur sowie die belegten Unterschiede zwischen den Plugin-Linien für Shopware 6.7 und für 6.5/6.6
Textbausteine: Technische Details
Live Demo: Teste dieses und weitere Plugins live — ohne Installation, ohne Risiko: demo.markus-michalski.net
Ablauf der Ersetzung
graph TD
A["Twig-Filter mmd_snippets"] --> B["SnippetReplacer"]
B --> C["SnippetParser: Regex sucht Platzhalter"]
C --> D["SnippetRegistry: ein Abruf je Identifier"]
D --> E{"Im Cache?"}
E -->|"Ja"| G["Inhalt aus dem Cache"]
E -->|"Nein"| F["Repository-Suche: Identifier und aktiv"]
F --> G
G --> H["HtmlSanitizer bereinigt den Inhalt"]
H --> I["Platzhalter wird ersetzt"]
| Schritt | Klasse | Verhalten |
|---|---|---|
| Filter | Mmd\ProductSnippets\Twig\SnippetExtension |
Filter mmd_snippets und Funktion mmd_snippet. Holt den Kontext aus der aktuellen Anfrage. Ohne Anfrage wird der Text unverändert zurückgegeben. |
| Ersetzen | Mmd\ProductSnippets\Core\Content\Snippet\SnippetReplacer |
Sammelt die Identifier, lädt die Bausteine, ersetzt per Regex und bereinigt jeden Inhalt mit dem HtmlSanitizer. Platzhalter ohne Treffer (unbekannt, inaktiv oder ohne Inhalt in der aktuellen Sprache) bleiben stehen. |
| Parsen | Mmd\ProductSnippets\Core\Content\Snippet\SnippetParser |
Regex {{ snippet:<identifier> }} bzw. {{ textbaustein:<identifier> }}, Identifier aus a-z, A-Z, 0-9, - und _, ohne Beachtung der Groß-/Kleinschreibung. |
| Laden | Mmd\ProductSnippets\Core\Content\Snippet\SnippetRegistry |
Cache-Schicht vor dem Repository mmd_product_snippet. |
Caching
- Die Registry ruft für jeden verschiedenen Identifier einer Beschreibung einmal den Cache ab. Bei einem Cache-Fehlzugriff folgt für diesen Identifier eine Repository-Suche. Es gibt keine gemeinsame Abfrage für mehrere Identifier.
- Verwendet wird der Cache-Pool
cache.object. Der Schlüssel enthält Identifier und Sprach-ID, jede Sprache hat also einen eigenen Eintrag. - Die Lebensdauer beträgt 3600 Sekunden. Auch ein nicht gefundener Baustein wird so lange zwischengespeichert.
- Das Plugin versieht jeden Eintrag mit Cache-Tags (Identifier und alle Bausteine) und invalidiert über diese Tags, sofern der Cache-Adapter Tags unterstützt. Ohne Tag-Unterstützung löscht es nur den Schlüssel ohne Sprach-ID, was die Einträge der einzelnen Sprachen nicht trifft, und das Invalidieren aller Einträge entfällt. Die Einträge laufen dann erst nach der Lebensdauer ab.
Invalidierung
Ein Subscriber (Mmd\ProductSnippets\Subscriber\CacheInvalidationSubscriber) hört auf die Entity-Events mmd_product_snippet.written und mmd_product_snippet.deleted:
- Geschrieben: Der Cache-Eintrag des Identifiers wird nur invalidiert, wenn die Schreiboperation den Identifier enthält, also beim Anlegen.
- Gelöscht: Alle Baustein-Einträge werden invalidiert.
- Inhalt oder Name geändert: Diese Felder liegen in der Übersetzungstabelle. Ein Speichern, das nur sie ändert, enthält keinen Identifier und löst keine Invalidierung aus.
- Aktiv umgeschaltet: Auch hier enthält die Schreiboperation nur das Feld
active, es gibt keine Invalidierung. Ein deaktivierter Baustein bleibt deshalb weiter sichtbar, ein neu aktivierter bleibt als "nicht gefunden" im Cache.
In den letzten beiden Fällen erscheint die Änderung spätestens nach Ablauf der 3600 Sekunden oder sofort nach bin/console cache:clear. Das Plugin invalidiert außerdem nicht den HTTP-Cache der Produktseiten.
Datenmodell
| Tabelle | Inhalt |
|---|---|
mmd_product_snippet |
id, identifier (VARCHAR 255, UNIQUE, Collation utf8mb4_unicode_ci), active (Standard 1), Zeitstempel |
mmd_product_snippet_translation |
mmd_product_snippet_id, language_id, name, content (LONGTEXT), Zeitstempel. Löschen des Bausteins löscht kaskadierend die Übersetzungen. |
Die Entity heißt mmd_product_snippet (Felder identifier, active, name, content). Alle Felder sind für die Admin-API freigegeben, sodass sich Bausteine auch darüber lesen und schreiben lassen. Eine Validierung des Identifiers gibt es dort ebenfalls nicht.
Plugin-Struktur
src/
├── MmdProductSnippets.php # Plugin-Klasse, löscht Tabellen bei Deinstallation
├── Content/Snippet/ # Entity-Definitionen (Baustein und Übersetzung)
├── Core/Content/Snippet/ # Parser, Replacer, Registry
├── Migration/ # Legt die beiden Tabellen an
├── Subscriber/ # CacheInvalidationSubscriber
├── Twig/ # SnippetExtension (Filter, Funktion)
└── Resources/
├── app/administration/ # Admin-Modul (Liste und Detailseite)
├── config/services.xml
└── views/storefront/ # Drei Template-Overrides, unter Shopware 6.7 sind zwei wirksam
Von den drei Template-Overrides ist storefront/page/product-detail/description.html.twig unter Shopware 6.7 inaktiv, weil der Kern dieses Template nicht mehr bereitstellt. Details stehen auf der Seite Storefront-Integration.
Unterschiede der Plugin-Linie für Shopware 6.5 und 6.6
Die Linie mmd/sw66-product-snippets (Shopware ~6.5.0 || ~6.6.0) hat denselben Funktionsumfang und im Wesentlichen denselben Quelltext. Beim Vergleich beider Linien unterscheiden sich vor allem diese Punkte:
| Bereich | Linie 6.7 | Linie 6.5 / 6.6 |
|---|---|---|
| Composer-Paket und Shopware-Anforderung | mmd/sw67-product-snippets, ~6.7.0 |
mmd/sw66-product-snippets, ~6.5.0 \|\| ~6.6.0 |
Inhalt auslesen (SnippetRegistry) |
Direkter Zugriff auf das Feld content der Entity (nur die aktuelle Sprache) |
getTranslation('content') (liest das Array translated, das die Fallback-Kette der Sprachen einbezieht) |
| Admin-Seiten (Liste und Detail) | Seitentitel über head(), Mixins über Shopware.Mixin.getByName() |
Seitentitel über metaInfo(), Mixins aus dem destrukturierten Mixin |
| Mitgeliefertes Admin-Bundle | Vite-Build unter Resources/public/administration/assets |
Dateien unter Resources/public/administration/js und .../css |
| Klassen der Entity-Schicht | final, Eigenschaften identifier und mmdProductSnippetId mit typisiertem Standardwert '' |
nicht final, Eigenschaften ohne Standardwert |
Kommentar im Template box-standard.html.twig |
Anmerkung zu \|raw |
ohne diese Anmerkung (nur Kommentar, kein Verhalten) |
Der Unterschied beim Auslesen wirkt sich auf das Verhalten aus: Fehlt der Inhalt in der aktuellen Sprache, kann die Linie 6.5/6.6 auf die Standardsprache zurückfallen, während in der Linie 6.7 der Platzhalter unersetzt stehen bleibt. Dass
getTranslation()die Fallback-Kette berücksichtigt, ist Verhalten des Shopware-Kerns und lässt sich im Quelltext des Plugins nicht belegen.
Dienstdefinitionen (services.xml), Migration, Tabellen, Platzhalter-Syntax, Twig-Filter und -Funktion, die Template-Dateien und die Admin-Texte stimmen in beiden Linien überein.