Sylius SEO Suite Pro: Technik
Technik von Sylius SEO Suite Pro: SeoData-Pipeline als Diagramm, acht Tabellen mit ER-Diagramm, Migrationen, Plugin-Struktur, Request-Lebenszyklus, Namenskonventionen und Tests.
Sylius SEO Suite Pro: Technik
Diese Seite beschreibt den Aufbau des Plugins für Integratoren: den Datenfluss einer Anfrage, die Datenbanktabellen, die Verzeichnisstruktur und die Namenskonventionen. Erweiterungspunkte stehen unter Erweiterbarkeit.
Architektur: die SeoData-Pipeline
Storefront und Shop API holen ihre Daten aus derselben Quelle, dem SeoDataProviderInterface. Dadurch können beide Frontends nicht auseinanderlaufen. Das Ergebnis ist ein unveränderliches SeoData-Objekt mit Meta-Daten, Open Graph, Twitter Card, strukturierten Daten und hreflang-Alternativen.
graph TD
REQ["Storefront-Request"] --> HOOK["Twig Hook sylius_shop.base#metatags"]
HOOK --> FN["Twig-Funktion mmd_seo_suite_pro_seo"]
FN --> CTX["RequestSubjectResolver: Produkt, Kategorie oder Startseite"]
CTX --> PROV["SeoDataProviderInterface"]
API["API Platform: GET /shop/seo/products/code und /shop/seo/taxons/code"] --> APIP["SeoResourceProvider"]
APIP --> PROV
PROV --> META["SubjectMetaDataResolver: Meta-Titel, Beschreibung, Canonical, Robots"]
META --> TPL["SEO-Template-Renderer und Platzhalter"]
META --> FACET["FacetRobotsPolicy"]
PROV --> SCHEMA["StructuredDataBuilder: Schema-Provider und Enricher"]
PROV --> SOCIAL["SocialTagsFactory und OgImageCardUrlResolver"]
PROV --> HREF["HreflangAlternatesBuilder"]
META --> DATA["SeoData"]
SCHEMA --> DATA
SOCIAL --> DATA
HREF --> DATA
DATA --> TWIG["Template shop/metatags.html.twig"]
DATA --> RES["API-Ressource Seo"]
TWIG --> TITLE["TitleListener ersetzt title bei kernel.response"]
Die Schritte im Einzelnen:
- Der Hook
sylius_shop.base#metatagsruft die Twig-Funktionmmd_seo_suite_pro_seo()auf. DerRequestSubjectResolverfindet das Objekt der Seite erneut über Route und Slug (sylius_shop_product_show,sylius_shop_product_index), weil der Hook fast ohne Twig-Kontext läuft. Auf allen anderen Seiten (Warenkorb, Checkout, Konto) liefert die Funktionnull, und die Startseite wird übersylius_shop_homepageerkannt. - Der Provider setzt die Teile zusammen: Meta-Daten samt Templates, Facet-Robots für gefilterte Listings, JSON-LD-Knoten, Open Graph und Twitter Card (mit der URL einer generierten Karte, falls es eine gibt) sowie die hreflang-Alternativen. Für hreflang gilt: nicht bei
noindex-Seiten, nicht bei gefilterten Listings und nicht ab Seite 2. - Das Template
shop/metatags.html.twigrendert Meta-Tags, Open Graph, Twitter, Canonical, hreflang und je einen JSON-LD-Block (übermmd_seo_suite_pro_json_ld(), das gegen ein Ausbrechen aus dem Script-Tag escaped). - Der
<title>wird erst im fertigen HTML ersetzt, weil das Sylius-Layout ihn über einen Twig-Block statt über einen Hook ausgibt. - Die Shop API (
/shop/seo/products/{code},/shop/seo/taxons/{code}) nutzt denselben Provider. Siehe Shop API.
Tabellen und Datenmodell
Das Plugin legt neun Tabellen an. Alle tragen das Präfix mmd_sylius_seo_suite_pro_, und keine hat einen Fremdschlüssel zu Sylius-Core-Tabellen. Die Bindung an einen Channel ist rein logisch über die Spalte channel_code.
erDiagram
SYLIUS_CHANNEL ||--o| CHANNEL_SETTINGS : channel_code
SYLIUS_CHANNEL ||--o{ SEO_TEMPLATE : channel_code
SYLIUS_CHANNEL ||--o| HREFLANG_SETTINGS : channel_code
SYLIUS_CHANNEL ||--o| SCHEMA_SETTINGS : channel_code
SYLIUS_CHANNEL ||--o| OG_IMAGE_SETTINGS : channel_code
SYLIUS_CHANNEL ||--o{ REDIRECT : channel_code
SYLIUS_CHANNEL ||--o{ NOT_FOUND_LOG : channel_code
SYLIUS_CHANNEL ||--o{ AUDIT_RUN : channel_code
AUDIT_RUN ||--o{ AUDIT_ISSUE : run_id
Das Diagramm zeigt nur logische Beziehungen. Weder zwischen Plugin und Sylius noch zwischen Audit-Lauf und Befund gibt es Datenbank-Fremdschlüssel, die Zuordnung läuft über
channel_codebeziehungsweiserun_id. Bei einem Redirect istchannel_codeoptional (leer bedeutet alle Channels).
| Entity | Tabelle | Zweck |
|---|---|---|
ChannelSettings |
mmd_sylius_seo_suite_pro_channel_settings |
Einstellungen pro Channel: robots.txt-Inhalt, Facet-Robots, Social Tags, Open-Graph- und Twitter-Angaben |
SeoTemplate |
mmd_sylius_seo_suite_pro_template |
Titel- und Beschreibungs-Templates je Channel, Sprache (leer = alle) und Typ |
HreflangSettings |
mmd_sylius_seo_suite_pro_hreflang_settings |
hreflang je Channel: Aktivierung, Gruppe, x-default-Sprache |
SchemaSettings |
mmd_sylius_seo_suite_pro_schema_settings |
Attribut-Zuordnung für GTIN, MPN, Marke und zusätzliche Eigenschaften im Product-Knoten |
OgImageSettings |
mmd_sylius_seo_suite_pro_og_image_settings |
Gestaltung und Schalter der generierten Social-Karten je Channel |
Redirect |
mmd_sylius_seo_suite_pro_redirect |
Weiterleitungen (manuell, automatisch, Import) mit Trefferzähler |
NotFoundLog |
mmd_sylius_seo_suite_pro_not_found_log |
Protokoll der 404-Pfade ohne Redirect |
AuditRun |
mmd_sylius_seo_suite_pro_audit_run |
Ein Audit-Lauf je Channel mit Status, Heartbeat und Sperre (intern, per SQL) |
AuditIssue |
mmd_sylius_seo_suite_pro_audit_issue |
Einzelner Befund eines Audit-Laufs |
Die Entities (und ihre Interfaces) liegen unter src/Entity. Die Ressourcen sind bei Sylius mit dem Alias-Präfix mmd_sylius_seo_suite_pro_plugin registriert.
Migrationen
Es gibt acht Migrationen in src/Migrations, eine je Funktionsblock (Channel-Einstellungen, Social-Spalten der Channel-Einstellungen, Templates, Redirects mit 404-Log, hreflang, Schema, Audit, OG-Bilder). Sie sind ausschließlich für MySQL 8 und MariaDB geschrieben: Auf jeder anderen Plattform überspringt sich jede Migration selbst (skipIf) und wird als ausgeführt vermerkt, die Tabellen entstehen dann nicht.
Die Pfadspalten von Redirect (source, source_key, target) und NotFoundLog (path, path_key) verwenden die Kollation utf8mb4_bin. Der Vergleich von Redirect-Quellen läuft über den kleingeschriebenen source_key, so dass er bei Groß- und Kleinschreibung nicht unterscheidet, während der Originalpfad exakt erhalten bleibt. Die Sperre des Audits beruht auf dem eindeutigen lock_key, der nach einem Lauf auf NULL gesetzt wird.
Ausgeführt werden die Migrationen mit bin/console mmd:seo-suite-pro:setup. Der Befehl führt nur die offenen Migrationen dieses Plugins aus, nie die des Hosts, und verlangt, dass die Sylius-Core-Migrationen vorher durch sind. Doctrines Tabelle doctrine_migration_versions ist die einzige Quelle der Wahrheit. Siehe Installation.
Struktur des Plugins
sylius-seo-suite-pro/
├── src/
│ ├── Api/ API-Platform-Ressource Seo und State Provider
│ ├── Audit/ Audit-Lauf, Store, Dashboard und die Checks (Check/)
│ ├── Command/ Konsolenbefehle (setup, audit, og-image, redirects)
│ ├── Controller/ /robots.txt, Karten-Route /seo/og/... und Admin-Controller (Admin/)
│ ├── DependencyInjection/ Konfigurationsbaum und Extension des Bundles
│ ├── Doctrine/ Listener für automatische Redirects bei Slug-Änderung
│ ├── Entity/ Doctrine-Entities und ihre Interfaces
│ ├── EventListener/ 404-Redirect, Titelersatz, Ressourcen-Listener für Redirects
│ ├── Factory/ vorbelegter Redirect aus dem 404-Log
│ ├── Form/ Admin-Formulare (Type/) und SEO-Felder an den Übersetzungsformularen (Extension/)
│ ├── Grid/ eigene Grid-Filter
│ ├── Hreflang/ hreflang-Provider, Builder, URL-Bau und Einstellungs-Lookup
│ ├── Menu/ Eintrag in der Admin-Sidebar
│ ├── Migrations/ die acht Migrationen
│ ├── Model/ Interfaces und Traits für die Übersetzungsklassen des Shops
│ ├── OgImage/ Planer, Quelle laden, Renderer (GD), Datei-Cache, Prune
│ ├── Redirect/ Pfadregeln, Store, Ketten, 404-Recorder, CSV-Import und -Export
│ ├── Schema/ Schema-Provider (JSON-LD), Enricher, GTIN-Prüfung, Attribut-Lader
│ ├── Seo/ Meta-Daten, SeoData, Provider, Template-Renderer, Facets, Sitemap-Ausschluss
│ ├── Settings/ Repositories für die Einstellungs-Entities
│ ├── Twig/ Twig-Funktionen und -Filter
│ ├── Validator/ Constraints für Templates, Redirects, hreflang und Schema
│ └── MmdSyliusSeoSuiteProPlugin.php Bundle-Klasse
├── config/
│ ├── config.yaml Bundle-Grundkonfiguration
│ ├── services.php Service-Definitionen und Tags
│ ├── grids/ Admin-Grids
│ ├── routes/ admin.yaml und shop.yaml (vom Host zu importieren)
│ └── twig_hooks/ Hook-Registrierungen für Admin und Shop
├── Resources/fonts/ mitgelieferte Schriften DejaVu Sans und Sans Bold für die OG-Karten
├── templates/
│ ├── admin/ Admin-Templates (Audit, Redirects, Formulare, Grid-Aktionen)
│ └── shop/metatags.html.twig Head-Template für die Storefront
├── translations/ messages und validators in Deutsch und Englisch
└── tests/ Unit, Integration, Functional, Behat und TestApplication
Lebenszyklus einer Storefront-Anfrage
Bei jeder Produkt-, Kategorie- und Startseite läuft die SeoData-Pipeline genau einmal. Das Ergebnis liegt als Attribut an der Hauptanfrage (_mmd_seo_suite_pro_data), weitere Aufrufe der Twig-Funktion im selben Request kosten nichts. Der Titel wird erst bei kernel.response in das fertige HTML eingesetzt.
Kosten und Zwischenspeicher pro Anfrage:
- Templates: Die Template-Zeilen des aktuellen Channels werden mit einer einzigen Abfrage geladen und im Speicher gehalten. Beliebig viele Anfragen nach Titel und Beschreibung nutzen diesen Speicher. Nur wenn ein Template einen
{attribute.*}-Platzhalter nutzt, kommt eine weitere Abfrage für die Attributwerte des Produkts dazu, und auch die läuft pro Seite nur einmal. - Einstellungen: Die Lookups für hreflang, Schema und OG-Bilder lesen ihre Zeilen ebenfalls einmal und halten sie im Speicher. Die Channel-Liste für hreflang wird einmal pro Request geladen. Die Speicher werden zwischen Requests über
kernel.resetgeleert. - OG-Bilder: Beim Bau der Seite wird nur die Karten-URL samt Hash aus Daten im Speicher berechnet. Es wird kein Bild gelesen, geladen oder gezeichnet. Gezeichnet wird bei der ersten Anfrage der Karten-URL oder vorab mit
mmd:seo-suite-pro:og-image:generate. - Fehlertoleranz: Fehlende Tabellen und Fehler beim Aufbau von hreflang oder einzelner Schema-Knoten legen die Seite nicht lahm. Sie werden geloggt und die Ausgabe entfällt.
- 404-Behandlung: Nur wenn eine 404 auftritt (
kernel.exception, Priorität 10, nur GET und HEAD in der Storefront), kommt eine Abfrage für den Redirect hinzu, bei einem Treffer das Hochzählen, sonst der Eintrag im 404-Log. Admin,/apiund/_*sind ausgenommen. - Slug-Änderungen: Ein Doctrine-Listener (
onFlush,postFlush) legt Redirects an. Fehler dort werden geloggt und blockieren das Speichern nie. - Nicht auf Shop-Requests: Das Audit, die Prune-Befehle und der Import und Export laufen ausschließlich in der Konsole oder im Admin.
Namenskonventionen
| Bereich | Präfix oder Muster | Beispiel |
|---|---|---|
| Konsolenbefehle | mmd:seo-suite-pro: |
mmd:seo-suite-pro:audit:run |
| Datenbanktabellen | mmd_sylius_seo_suite_pro_ |
mmd_sylius_seo_suite_pro_redirect |
| Konfigurationswurzel | mmd_sylius_seo_suite_pro |
config/packages/mmd_sylius_seo_suite_pro.yaml |
| Service-IDs, Parameter, Übersetzungsschlüssel | mmd_sylius_seo_suite_pro. |
mmd_sylius_seo_suite_pro.sitemap_exclusion |
| Service-Tags (Erweiterungspunkte) | mmd_sylius_seo_suite_pro.<art> |
mmd_sylius_seo_suite_pro.schema_provider |
| Manuelle Admin-Routen | mmd_sylius_seo_suite_pro_admin_ |
mmd_sylius_seo_suite_pro_admin_audit |
| Generierte Ressourcen-Routen | mmd_sylius_seo_suite_pro_plugin_admin_<ressource>_<aktion> |
mmd_sylius_seo_suite_pro_plugin_admin_seo_redirect_index |
| Shop-Routen | mmd_sylius_seo_suite_pro_ |
mmd_sylius_seo_suite_pro_robots_txt, mmd_sylius_seo_suite_pro_og_image |
| Eigene Hook-Wurzeln | mmd_sylius_seo_suite_pro. |
mmd_sylius_seo_suite_pro.seo_audit.index |
| Hook-Schlüssel innerhalb von Core-Hooks, Twig-Funktionen | mmd_seo_suite_pro_ |
mmd_seo_suite_pro_seo |
| Ressourcen-Alias und UI-Schlüssel | mmd_sylius_seo_suite_pro_plugin |
mmd_sylius_seo_suite_pro_plugin.ui.<ressource> |
Die Ressourcennamen tragen ein Präfix (zum Beispiel seo_suite_pro_channel_settings), weil Sylius die Admin-Hooks nur nach dem Ressourcenteil des Alias benennt. Ein allgemeiner Name würde die Buttons dieses Plugins auf Seiten anderer Plugins zeigen. Admin-URLs und Tabellennamen sind davon nicht betroffen.
Die Admin-Routen liegen unter %sylius_admin.path_name%, nicht unter einem festen /admin. Die Shop-Routen (/robots.txt und /seo/og/...) gehören auf die oberste Ebene und nicht unter das Locale-Präfix.
Als
@internalgekennzeichnete Klassen (zum Beispiel die Admin-Controller und einzelne Listener) sind keine Erweiterungs-API. Alles, was nicht in den Interfaces der Seite Erweiterbarkeit steht, gilt als Implementierungsdetail. Erweitere über die getaggten Interfaces, siehe Erweiterbarkeit.
Entwicklung und Tests
Für Integratoren ist nur wenig davon relevant. Das Repository bringt eine Docker-Umgebung mit, ein lokales PHP ist nicht nötig:
make init # compose.override.yml kopieren, composer install, Assets bauen, Container starten
make static # PHPStan und ECS
make phpunit # Unit-, Integration- und Functional-Tests
make php-shell # Shell im PHP-Container
Die Integrations- und Functional-Tests bauen das Schema selbst aus dem ORM-Mapping und brauchen nur eine erreichbare Datenbank (APP_ENV=test). Unter tests/TestApplication liegen die Produkt- und Kategorie-Unterklassen samt Migration, wie ein Shop sie für das Zuschalten der SEO-Felder schreibt. Das ist die Referenz zur Anleitung in der Installation.