Markus Michalski FAQ

Contact Requests: Technische Details

Sylius Contact Requests Plugin - Architektur der beiden Mailer-Decorators, Plugin-Struktur, Tabelle und Migration, Verhalten beim Protokollieren von Speicherfehlern

English Version

Zurück zur Übersicht

Contact Requests: Technische Details

Live Demo: Teste dieses und weitere Plugins live — ohne Installation, ohne Risiko: demo.markus-michalski.net

Architektur

graph LR
    A["Twig-Kontaktformular (Shop)"] --> D["sylius_shop.mailer.contact_email_manager"]
    B["Shop API SendContactRequest"] --> E["sylius.mailer.contact_email_manager"]
    D --> F["PersistingContactEmailManager (Decorator)"]
    E --> F
    F --> G["ContactRequest speichern"]
    F --> H["Ursprünglicher Manager: E-Mail versenden"]

Sylius bringt für den Versand der Kontaktanfragen zwei voneinander unabhängige Services desselben Mailers (ContactEmailManager, Interface ContactEmailManagerInterface) mit: sylius.mailer.contact_email_manager nutzt der Shop-API-Pfad, sylius_shop.mailer.contact_email_manager das klassische Twig-Kontaktformular, das ShopBundle eigenständig registriert. Das Plugin dekoriert beide:

Der zweite Service ist ein Implementierungsdetail des ShopBundle und kein offizieller Vertrag. Der Compiler-Pass prüft deshalb mit hasDefinition(), ob er existiert. Fehlt er in einer künftigen Sylius-Version, speichert das Plugin nur noch Anfragen über die Shop API, statt den Container-Build scheitern zu lassen.

Ausfallsicheres Speichern

Schlägt das Speichern fehl, schließt Doctrine den Entity Manager (das passiert nach jedem gescheiterten flush()). Der Decorator ruft deshalb ManagerRegistry::resetManager() auf. Sonst würde jeder spätere Schreibzugriff in derselben Anfrage mit „The EntityManager is closed“ scheitern.

Protokolliert werden nur Exception-Klasse und Code, nie getMessage() und nie das Exception-Objekt:

Failed to persist contact request: Doctrine\DBAL\Exception\... (code 0)

Der Grund: Bei Konvertierungsfehlern enthält die Doctrine-Meldung oft den fehlerhaften Feldwert im Klartext, und für dieses Log gibt es keine eigene Aufbewahrungsfrist.

Plugin-Struktur

src/
├── Command/            # PurgeContactRequestsCommand, SetupCommand
├── DependencyInjection/
│   └── Compiler/        # DecorateShopContactEmailManagerPass
├── Entity/              # ContactRequest, ContactRequestInterface
├── Mailer/              # PersistingContactEmailManager (für beide Services)
├── Menu/                # Eintrag in der Admin-Sidebar
├── Migrations/          # Eine Doctrine-Migration
├── Repository/          # ContactRequestRepository (purgeOlderThan)
└── Twig/                # ContactRequestsExtension (Versionsanzeige im Admin)
config/
├── grids/               # Ein Admin-Grid
├── routes/              # admin.yaml (shop.yaml ist leer, es gibt keine eigenen Shop-Routen)
└── twig_hooks/          # Titelzeile und Detailansicht im Admin
templates/
└── admin/contact_request/   # Templates für Titelzeile und Detailansicht

Tabelle und Migration

Eine Tabelle, eine Migration: ContactRequest liegt in mmd_sylius_contact_requests_contact_request (Version20260904121117). Die Spalten sind id, createdAt (mit Index), name (optional), email, subject (optional), message (LONGTEXT) und consent (BOOLEAN, Standard false).

Versionsanzeige im Admin

Die Titelzeile im Admin zeigt die installierte Plugin-Version (mmd_contact_requests_plugin_version(), liest Composer\InstalledVersions). Bei einem getaggten Packeton-Release ist das die Semver-Nummer, bei einer lokalen Pfad-Installation dev-main plus die ersten sieben Zeichen des Commit-Hashes.