Sylius SEO Suite Pro: Strukturierte Daten
JSON-LD für Produkt, Breadcrumb, Organization und WebSite. GTIN, MPN, Marke und additionalProperty aus Produktattributen pro Channel, Twig-Funktion mmd_seo_suite_pro_json_ld, Einstellungen und Shop API.
Sylius SEO Suite Pro: Strukturierte Daten
Das Plugin gibt auf Produkt-, Kategorie- und Startseiten schema.org-Daten als JSON-LD aus. Jeder schema.org-Typ ist eine eigene SchemaProviderInterface-Implementierung. Zusätzlich kannst Du pro Channel festlegen, welche Produktattribute in den Product-Knoten einfließen: GTIN, MPN, Marke und weitere Eigenschaften (additionalProperty).
Alles, was ein Provider ausgibt, muss auf der Seite auch sichtbar sein. Sonst ignorieren Suchmaschinen es, und eine Bewertung, die niemand sehen kann, führt zu Abstrafungen.
Eingebaute Provider
Jeder Service, der SchemaProviderInterface implementiert, wird automatisch mit mmd_sylius_seo_suite_pro.schema_provider getaggt. Über die priority des Tags steuerst Du die Reihenfolge, höhere Werte kommen zuerst.
| Provider | Seite | Priorität | Hinweise |
|---|---|---|---|
ProductSchemaProvider |
Produkt | 30 | Offer (eine bepreiste Variante) oder AggregateOffer mit Preis, Währung, SKU und Verfügbarkeit aus dem Lagerbestand; brand aus dem konfigurierten Attribut; image. AggregateRating und Review nur aus Bewertungen, die ein Moderator freigegeben hat |
BreadcrumbListSchemaProvider |
Produkt, Kategorie | 20 | Derselbe Pfad wie die sichtbare Breadcrumb des Shops. Einträge, die der Shop ohne Link zeigt (Wurzelkategorie, deaktivierte Kategorien), entfallen, weil ein Listeneintrag eine URL braucht |
OrganizationSchemaProvider |
Startseite | 10 | Channel-Name, URL, Kontakt-E-Mail und Telefonnummer, falls gepflegt |
WebSiteSchemaProvider |
Startseite | 5 | Name, URL, Sprache (inLanguage, zum Beispiel de-DE) |
Ein Provider, der eine Ausnahme wirft, wird geloggt und übersprungen. Er kann die Seite nicht lahmlegen. Gibt ein Provider [] zurück, wird nichts ausgegeben.
graph TD
A["SchemaContext mit Seite, Channel, Locale und Meta-Daten"] --> B["StructuredDataBuilder"]
B --> C["ProductSchemaProvider, Priorität 30"]
C --> D["Product-Enricher nach Priorität"]
D --> E["Liste der JSON-LD-Knoten"]
B --> F["BreadcrumbList, Priorität 20"]
B --> G["Organization, Priorität 10, nur Startseite"]
B --> H["WebSite, Priorität 5, nur Startseite"]
F --> E
G --> E
H --> E
E --> I["Storefront - ein script-Tag pro Knoten"]
E --> J["Shop API - Feld structuredData"]
Der Name des Product-Knotens ist der aufgelöste Meta-Titel, seine Beschreibung die aufgelöste Meta-Beschreibung (oder, wenn leer, die Kurzbeschreibung des Produkts). Ein SEO-Template wirkt also auch hier.
Preise sind die kleinsten Einheiten von Sylius geteilt durch 100 (zwei Nachkommastellen), die Währung ist die Basiswährung des Channels. Eine Währung mit anderer Nachkommastellenzahl braucht einen eigenen Product-Provider.
Die Preise und Verfügbarkeiten im Product-Knoten stammen aus dem Channel der Seite. Nur Varianten mit Preis in diesem Channel erscheinen im Knoten.
Produktattribute im Product-Knoten
Produktattribute können den Product-Knoten füllen, damit die Kennungen und Spezifikationen, die ein Shop ohnehin pflegt, bei Suchmaschinen und KI-Shopping-Assistenten ankommen. Das wird pro Channel eingestellt: Der Button Strukturierte Daten auf dem Bildschirm SEO Suite Pro öffnet die Einstellungen. Es gibt eine Zeile pro Channel, ein Zurück-Button führt zu SEO Suite Pro, einen eigenen Menüeintrag gibt es nicht.
Einstellungen
| Einstellung | Bedeutung |
|---|---|
| Channel | Der Channel, für den die Zeile gilt. Pro Channel gibt es genau eine Zeile; bei einer bestehenden Zeile ist der Channel nicht änderbar |
| GTIN-Attribut | Ein Textattribut mit der GTIN |
| MPN-Attribut | Ein Textattribut mit der Hersteller-Teilenummer |
| Marken-Attribut | Ein Text- oder Auswahlattribut mit der Marke. Ein zugeordneter Wert hat Vorrang vor der Plugin-Einstellung brand_attribute_code. Ist nichts zugeordnet ("Standard"), bleibt dieses Ergebnis unverändert |
| Weitere Eigenschaften | Aus (Standard), Alle Attribute oder Nur ausgewählte Attribute |
| Aufzulistende Attribute | Nur im Modus Nur ausgewählte Attribute: mindestens ein Attribut |
| Nie aufgelistete Attribute | In den Modi Alle Attribute und Nur ausgewählte Attribute, zum Beispiel für interne Attribute |
Die Liste der Zeilen zeigt Channel, Modus, GTIN-, MPN- und Marken-Attribut und bietet Anlegen, Bearbeiten und Löschen. Die Daten liegen in der Tabelle mmd_sylius_seo_suite_pro_schema_settings.
Ordne nur Attribute zu, die die Produktseite auch anzeigt (Sylius listet Produktattribute standardmäßig auf der Produktseite auf). Suchmaschinen ignorieren strukturierte Daten, die niemand sieht. Das Formular weist darauf hin, das Plugin erzwingt es aber nicht.
Das Formular lässt nur passende Attribute zu und prüft beim Speichern:
| Meldung | Auslöser |
|---|---|
| Das Attribut {{ code }} gibt es nicht. | Ein Attribut-Code existiert nicht (mehr) |
| Das Attribut {{ code }} hat den Typ {{ type }}; hier sind nur {{ allowed }} möglich. | GTIN oder MPN nicht vom Typ text; Marke nicht text oder select |
| Wähle mindestens ein Attribut oder ändere den Modus. | Modus Nur ausgewählte Attribute ohne Attribut |
| Dieser Channel hat bereits SEO-Suite-Pro-Einstellungen. | Zweite Zeile für denselben Channel |
Für GTIN und MPN sind nur Textattribute erlaubt. Sylius speichert Integer-Attribute in einer 32-Bit-Spalte, die eine GTIN-12/13/14 nicht aufnehmen kann und führende Nullen verliert. Der Hilfetext im Formular erwähnt noch "Text oder Zahl", maßgeblich ist aber die Validierung: Es geht nur
text.
GTIN, MPN und Marke
- GTIN. Ausgegeben wird nur eine gültige GTIN: ausschließlich Ziffern (Leerzeichen und Bindestriche werden entfernt), Länge 8, 12, 13 oder 14 und eine korrekte GS1-Prüfziffer. Sie landet in der zur Länge passenden Eigenschaft (
gtin8,gtin12,gtin13odergtin14), also der spezifischsten Form. Ein ungültiger Wert wird weggelassen und einmal pro Prozess und Produktcode geloggt, ein leerer Wert wird ohne Logeintrag ignoriert. - MPN. Wird getrimmt und weggelassen (einmal geloggt), wenn sie länger als 70 Zeichen ist.
- Marke. Der Wert des zugeordneten Attributs ersetzt
brandim Knoten. Ohne Zuordnung kommt die Marke aus dem Plugin-Wertbrand_attribute_code(Standardbrand); hat das Produkt dieses Attribut nicht, gibt es keine Marke im Knoten. - Sprache. Ein Attributwert wird in der Sprache der Seite genommen, dann der sprachunabhängige, dann der in der Standardsprache des Channels.
additionalProperty
Pro Attribut entsteht ein PropertyValue. Der Name ist der Attributname in der Sprache der Seite (dann in der Standardsprache des Channels, dann irgendeine Übersetzung, dann der Code). Der Wert wird nach Attributtyp formatiert:
| Attributtyp | Wert |
|---|---|
| Text | getrimmt |
| Textarea (Rich-Text) | Tags (inklusive Inhalt von script und style) entfernt, Entities dekodiert |
| Integer | wie gespeichert |
| Prozent, Datum | im Format der Sprache (ein am Attribut konfiguriertes Datumsformat wird beachtet) |
| Checkbox | lokalisiertes Ja oder Nein (eine ausgeschaltete Checkbox bleibt erhalten) |
| Auswahl | die Labels, durch , getrennt |
Werte werden bei 500 Zeichen gekürzt, leere Werte übersprungen. Ein Attribut, das schon für GTIN, MPN oder Marke verwendet wird (auch das Standard-Markenattribut, wenn es die Marke liefert), wird nicht wiederholt. Ausgegeben werden höchstens schema.max_additional_properties Einträge (Standard 25, in der Anzeigereihenfolge der Attribute).
Modus für weitere Eigenschaften
Aus
Der Standard. Es entsteht kein additionalProperty. Sind außerdem weder GTIN, MPN noch Marke zugeordnet, bleibt das JSON-LD Byte für Byte so wie ohne diese Einstellung, und die Attributwerte werden nicht einmal geladen.
Alle Attribute
Alle Attribute des Produkts mit Wert sind Kandidaten, abzüglich der Liste Nie aufgelistete Attribute und der Attribute, die schon für GTIN, MPN oder Marke verwendet werden. Gedeckelt bei 25 Einträgen.
Nur ausgewählte Attribute
Kandidaten sind nur die Attribute aus Aufzulistende Attribute, ebenfalls abzüglich der Ausschlussliste. Die Reihenfolge im Knoten richtet sich nach der Anzeigereihenfolge der Attribute, nicht nach der Reihenfolge der Auswahl.
Varianten
Sylius-Attribute gehören zum Produkt, nicht zur Variante. GTIN, MPN, Marke und Eigenschaften sitzen deshalb nur am Product-Knoten. Die Offers der Varianten bleiben unberührt. Eine GTIN pro Variante braucht einen eigenen Enricher, siehe Erweiterungen.
Beispiel: vollständiger Product-Knoten
Einstellungen des Channels WOHNWELT: GTIN-Attribut ean, MPN-Attribut mpn, Modus Nur ausgewählte Attribute mit material, breite und schlaffunktion. Das Produkt hat zwei freigegebene Bewertungen (5 und 4 Sterne). Hier formatiert dargestellt, im HTML steht jeder Knoten kompakt in einem eigenen script-Tag:
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Ledersofa Milano – Casa Nova | Wohnwelt",
"description": "Dreisitzer aus pflanzlich gegerbtem Leder mit Federkern. Jetzt bei Wohnwelt bestellen.",
"sku": "MILANO-3S",
"image": ["https://www.wohnwelt.example/media/image/ab/cd/ledersofa-milano.jpg"],
"brand": {"@type": "Brand", "name": "Casa Nova"},
"offers": {
"@type": "Offer",
"url": "https://www.wohnwelt.example/de_DE/products/ledersofa-milano",
"sku": "MILANO-3S",
"price": "1299.00",
"priceCurrency": "EUR",
"availability": "https://schema.org/InStock"
},
"aggregateRating": {"@type": "AggregateRating", "ratingValue": "4.5", "reviewCount": 2},
"review": [
{
"@type": "Review",
"name": "Sehr bequem",
"reviewBody": "Hochwertig verarbeitet und schnell geliefert.",
"author": {"@type": "Person", "name": "Anna"},
"reviewRating": {"@type": "Rating", "ratingValue": 5}
},
{
"@type": "Review",
"author": {"@type": "Person", "name": "Anonymous"},
"reviewRating": {"@type": "Rating", "ratingValue": 4}
}
],
"gtin13": "4006381333931",
"mpn": "ML-2200-BR",
"additionalProperty": [
{"@type": "PropertyValue", "name": "Material", "value": "Echtleder"},
{"@type": "PropertyValue", "name": "Breite", "value": "220"},
{"@type": "PropertyValue", "name": "Schlaffunktion", "value": "Nein"}
]
}
Zu den Details:
aggregateRatingrechnet über alle freigegebenen Bewertungen. Inreviewstehen höchstens 10 Einträge.- Hat ein Review keinen Autor mit Vornamen, steht dort
Anonymous. Leere Felder eines Reviews (Titel, Text) entfallen. - Hat das Produkt mehrere bepreiste Varianten, wird aus
offerseinAggregateOffer:
"offers": {
"@type": "AggregateOffer",
"lowPrice": "1299.00",
"highPrice": "1499.00",
"priceCurrency": "EUR",
"offerCount": 2,
"offers": [
{"@type": "Offer", "url": "https://www.wohnwelt.example/de_DE/products/ledersofa-milano", "sku": "MILANO-3S", "price": "1299.00", "priceCurrency": "EUR", "availability": "https://schema.org/InStock"},
{"@type": "Offer", "url": "https://www.wohnwelt.example/de_DE/products/ledersofa-milano", "sku": "MILANO-3S-XL", "price": "1499.00", "priceCurrency": "EUR", "availability": "https://schema.org/OutOfStock"}
]
}
Ohne Zeile in den Einstellungen, oder wenn nichts zugeordnet ist und der Modus Aus gilt, bleibt der Product-Knoten so, wie das Plugin ihn ohne diese Einstellungen erzeugt.
Twig-Funktion mmd_seo_suite_pro_json_ld
Die Funktion mmd_seo_suite_pro_json_ld(array $node) wandelt einen schema.org-Knoten in den Inhalt eines <script type="application/ld+json">-Tags um. Die Ausgabe ist HTML-sicher: <, > und & werden als Unicode-Escapes (\u003C, \u003E, \u0026) ausgegeben, damit kein Wert das Script-Element schließen kann. Slashes und Unicode-Zeichen bleiben lesbar.
Das mitgelieferte Template shop/metatags.html.twig ist in den Hook sylius_shop.base#metatags eingehängt und gibt pro Knoten ein Script-Tag aus. Die Daten der aktuellen Seite liefert mmd_seo_suite_pro_seo() (null auf Seiten, die weder Produkt, Kategorie noch Startseite sind):
{% set seo = mmd_seo_suite_pro_seo() %}
{% if seo %}
{% for node in seo.structuredData %}
<script type="application/ld+json">{{ mmd_seo_suite_pro_json_ld(node) }}</script>
{% endfor %}
{% endif %}
Shop API / Headless: Dieselben Knoten stehen im Feld
structuredDatavonGET /api/v2/shop/seo/products/{code}undGET /api/v2/shop/seo/taxons/{code}, weil Storefront und API denselben Service nutzen. Das Feld ist eine Liste von JSON-LD-Knoten, siehe Shop API. Ein Headless-Frontend serialisiert sie selbst in einscript-Tag und muss dabei<,>und&ebenfalls maskieren.
Konfiguration
| Schlüssel | Standard | Bedeutung |
|---|---|---|
mmd_sylius_seo_suite_pro.brand_attribute_code |
brand |
Attribut-Code der Marke, wenn für den Channel kein Marken-Attribut zugeordnet ist |
mmd_sylius_seo_suite_pro.schema.max_additional_properties |
25 |
Obergrenze für additionalProperty (Minimum 1) |
mmd_sylius_seo_suite_pro.image_base_path |
/media/image/ |
Wo lokale Produktbilder ausgeliefert werden; Basis für image |
Alle Schlüssel stehen auf der Seite Konfiguration.
Kosten
Die Attributwerte eines Produkts werden mit einer Fetch-Join-Query pro Request geladen, geteilt mit den Platzhaltern der SEO-Templates, und die Einstellungen werden einmal pro Request gelesen. Die Zahl der Queries hängt nicht von der Zahl der Attribute ab.
Die Einstellungen liegen in der Tabelle
mmd_sylius_seo_suite_pro_schema_settings. Führemmd:seo-suite-pro:setupaus, siehe Installation. Bis dahin behalten Storefront und API die bisherige Ausgabe (eine Warnung im Log). Nur der Bildschirm Strukturierte Daten braucht die Tabelle.
Eigene Typen und Felder
Für einen neuen schema.org-Typ implementierst Du Mmd\SyliusSeoSuiteProPlugin\Schema\SchemaProviderInterface (supports(SchemaContext) und provide(SchemaContext): array). Für zusätzliche Felder am Product-Knoten, zum Beispiel eine GTIN pro Variante, implementierst Du ProductSchemaEnricherInterface (enrich(array $node, ProductInterface $product, SchemaContext $context): array). Beide werden automatisch getaggt (mmd_sylius_seo_suite_pro.schema_provider und mmd_sylius_seo_suite_pro.product_schema_enricher). Höhere Priorität läuft zuerst, ein Enricher sieht das Ergebnis der früheren, und ein Provider oder Enricher, der eine Ausnahme wirft, wird geloggt und übersprungen, sodass der Product-Knoten erhalten bleibt. Den eingebauten Attribut-Enricher mit Priorität 0 kannst Du so vor- oder nachschalten. Beispiele und Details stehen auf der Seite Erweiterungen.