Multistore-Module für PrestaShop 9: Lecks zwischen Datenbereichen verhindern

45 Views

Der gefährlichste Multistore-Fehler ist oft eine gültige Abfrage, der eine Bedingung fehlt. Sie liefert einen echten Datensatz, besteht gewöhnliche Typprüfungen und speichert erfolgreich — im Zustand des falschen Shops. Eine Entwicklungsinstallation mit einem einzigen Shop deckt den Fehler selten auf.

Zuverlässige Multistore-Module beginnen mit der Datenzugehörigkeit. Entscheiden Sie, welche Werte global sind, welche einer Shopgruppe gehören und welche einem einzelnen Shop. Übertragen Sie diese Entscheidung dann auf Schlüssel, Lese- und Schreibvorgänge, Hintergrundjobs und Caches.

Dies ist ein allgemeiner Artikel zur Modulentwicklung für PrestaShop 9. Er beschreibt keine neuen Verwaltungsfunktionen in 9.2, und die SQL-Beispiele verwenden eine bewusst illustrative Modultabelle statt eines angenommenen Produktionspräfixes oder Core-Schemas.

Wenn Arbeit mit festgelegtem Geltungsbereich im Hintergrund läuft, wenden Sie einen zuverlässigen cron-Job-Entwurf an und übertragen Sie die ausdrückliche Shopidentität in jede Ausführung.

Modellieren Sie die Zugehörigkeit vor dem Hinzufügen einer id_shop-Spalte

Das Hinzufügen von id_shop zu einer Tabelle macht das Modul nicht Multistore-sicher. Jede Operation muss es konsistent verwenden, und manche Angaben sollten überhaupt nicht pro Shop dupliziert werden.

Betrachten Sie ein Mapping externer Referenzen. Wenn das externe System demselben Produkt in jedem Shop eine andere Referenz zuweist, gehört der Shopbereich zur Mappingidentität. Wenn die Referenz tatsächlich global ist, kann ihre Duplizierung widersprüchliche Kopien erzeugen. Klären Sie zuerst die Semantik des externen Systems.

Trennen Sie Entitätszuordnung von Wertzugehörigkeit. Ein Produkt kann mehreren Shops zugeordnet sein, während ein bestimmter Modulwert zwischen ihnen abweicht. Umgekehrt kann eine einem Shop zugeordnete Entität auf einen global zugehörigen Datensatz verweisen.

Erstellen Sie für jeden Moduldatensatzbestand eine Zugehörigkeitstabelle:

Datenbestand Eigentümer Lesebereich Schreibbereich
Zugangsdaten des externen Ziels Gewählter Konfigurationsbereich Explizite effektive Konfiguration Autorisierter Kontext
Produktmapping Durch externe Identitätsregeln festgelegt Produkt und erforderlicher Bereich Dieselbe Identitätsgrenze
Jobfortschritt Eine logische Jobpartition Expliziter Shop-/Jobschlüssel Besitzender Worker
Gemeinsames Nachschlagevokabular Global, wenn tatsächlich gemeinsam genutzt Global Kontrollierte Administratoraktion

Die Tabelle ist ein Entwurfsartefakt. Sie sollte erklären, warum ein Bereich existiert, statt lediglich die am einfachsten hinzufügbaren Spalten abzubilden.

Begrenzen Sie Lesen und Schreiben mit derselben Identität

Ein häufiger Defekt ist ein korrekt eingegrenzter Lesevorgang, gefolgt von einer zu weit gefassten Aktualisierung. Ein anderer ist die Suche nach Produkt und Shop bei gleichzeitig nur auf das Produkt beschränkter Eindeutigkeit.

Die folgende Abfrage veranschaulicht das Lesen eines moduleigenen Mappings mit Parameterbindung. Der wörtliche Tabellenname ist ein Beispiel; ein echter Installer muss seinen vertrauenswürdigen Präfix aus der Anwendungskonfiguration und seinem eigenen angegebenen Schema ableiten.

SELECT external_reference
FROM demo_product_mapping
WHERE id_shop = :shop_id
  AND id_product = :product_id;

Der zugehörige Schreibvorgang muss dieselbe Zugehörigkeitsgrenze verwenden. Aktualisieren Sie nicht allein anhand von id_product nachdem Sie mit beiden Bedingungen gelesen haben. Der Aufrufer sollte ein validiertes Bereichsobjekt oder eine ausdrückliche Shopkennung übergeben, statt sich auf veränderlichen globalen Kontext innerhalb des Repositorys zu verlassen.

Dynamische Bezeichner brauchen eine andere Behandlung als Parameterwerte. Gebundene Parameter schützen Werte, keine beliebigen Tabellennamen. Akzeptieren Sie niemals einen Tabellennamen oder Präfix aus einem HTTP-Parameter, um eine generische Multistore-Abfrage zu ermöglichen.

Berücksichtigen Sie bei einer Stapelabfrage den Bereich in Joins ebenso wie im äußeren Prädikat. Ein Join nur über das Produkt kann die Modulzeile eines anderen Shops anhängen, selbst wenn die Hauptentitätsabfrage korrekt eingegrenzt ist.

Testen Sie den Negativfall ausdrücklich: Fragen Sie den Wert von Shop A ab, wenn nur Shop B einen besitzt. Kein lokales Mapping zurückzugeben kann korrekt sein; stillschweigend das Mapping von B zu übernehmen ist kein sicherer Standard.

Lassen Sie die Eindeutigkeit die Geschäftsregel ausdrücken

Wenn das Modell ein Mapping pro Produkt und Shop vorsieht, sollte sein Schlüssel dieses Paar ausdrücken. Ein eindeutiger Schlüssel nur auf dem Produkt lehnt gültige Datensätze eines anderen Shops ab. Ohne eindeutigen Schlüssel sind bei gleichzeitigen Einfügungen Duplikate möglich.

Eine illustrative Modultabellendefinition lautet:

CREATE TABLE demo_product_mapping (
    id_shop INT UNSIGNED NOT NULL,
    id_product INT UNSIGNED NOT NULL,
    external_reference VARCHAR(191) NOT NULL,
    PRIMARY KEY (id_shop, id_product)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Dies ist kein Import für eine produktive PrestaShop-Datenbank. Er demonstriert nur den Zugehörigkeitsschlüssel. Ein echtes Schema benötigt die Installationskonventionen des Moduls, geprüfte Kennungstypen, eine Migrationsstrategie und weitere Beziehungseinschränkungen.

Fügen Sie keine Eindeutigkeit für external_reference hinzu, ohne zu entscheiden, ob die Referenz global, shoplokal oder ziellokal eindeutig ist. Wenn mehrere externe Systeme dieselbe Zeichenfolge zuweisen können, kann auch die Zielidentität zum Schlüssel gehören.

Prüfen und bereinigen Sie Duplikate nach einer festgelegten Richtlinie, bevor Sie bestehende Daten um eine Einschränkung ergänzen. Die zufällig zweite Zeile zu löschen ist keine Migrationsstrategie. Bewahren Sie Nachweise auf, welcher Datensatz behalten wurde und warum.

Eine Datenbankeinschränkung ergänzt die Anwendungsvalidierung. Sie schützt die Invariante, wenn zwei gültig wirkende Anfragen miteinander konkurrieren.

Behandeln Sie geerbte Konfiguration als Zustand, nicht nur als Wert

Ein Shop kann einen Wert erben oder ausdrücklich einen gleichen Wert speichern. Bei Änderungen des übergeordneten Werts verhalten sie sich unterschiedlich. Ein Modul, das den effektiven Wert liest und bedingungslos zurückschreibt, kann Vererbung versehentlich in eine lokale Überschreibung verwandeln.

Die Konfigurationsdokumentation von PrestaShop beschreibt bereichsbewussten Zugriff mit ShopConstraint, und die Formularhinweise unterscheiden lokale Konfiguration von geerbten Werten. Folgen Sie dem relevanten Dienstvertrag für die unterstützte Plattformversion. Shopkonfiguration, Konfigurationsformulare.

Unterscheiden Sie beim Implementieren des Speicherns zwischen „lokalen Wert setzen“, „lokale Überschreibung entfernen“ und „dieses Feld unverändert lassen“. Diese Vorgänge sollten nicht in einer einzigen Wertzuweisung zusammenfallen.

Dieselbe Sorgfalt gilt für Zugangsdaten und externe Ziele. Ein Worker sollte wissen, welche effektive Konfiguration er verwendet hat und welchen Shop er verarbeiten durfte. Das Loggen der Zielkennung kann Bereichsprobleme diagnostizieren helfen, ohne die Zugangsdaten selbst offenzulegen.

Erfinden Sie nicht id_shop = 0 als universelle Konvention für globale Zeilen in jedem Datenbestand. Core- und Modulspeichermodelle unterscheiden sich. Folgen Sie dem konkreten Schema und der Dienstsemantik, statt einen undokumentierten Sonderwert im gesamten Code zu verbreiten.

Übertragen Sie den Bereich in eingereihte und geplante Arbeit

Ein Hintergrundjob sollte genügend Identität enthalten, um ohne Mitarbeitersitzung ausgeführt zu werden: Shop, Operation, relevante Entitätskennungen und einen stabilen fachlichen Schlüssel. Validieren Sie den Shop vor dem Laden von Daten oder Konfiguration.

Reihen Sie nicht nur eine Produkt-ID ein und lassen Sie einen späteren Worker den Shop aus seinem zufällig vorhandenen globalen Kontext ableiten. Langlebige Worker können mehrere Nachrichten verarbeiten, wodurch durchgesickerter Kontext besonders schwer zu reproduzieren ist.

Wenn Legacy-Code impliziten Umgebungskontext benötigt, isolieren Sie die Anpassung sorgfältig und stellen Sie den vorherigen Zustand auch bei einer Ausnahme wieder her. Bevorzugen Sie ausdrücklichen Bereich an Ihren eigenen Dienstgrenzen, damit sich die Legacy-Abhängigkeit nicht im gesamten Modul ausbreitet.

Ein Job für alle Shops sollte eine bewusst festgelegte autorisierte Menge durchlaufen und für jede Partition den passenden Bereich herstellen und löschen. „Alle Shops“ sollte eine ausdrückliche Operation mit definiertem Ergebnis sein, nicht die versehentliche Folge eines fehlenden Arguments.

Nehmen Sie den Bereich in Sperr- und Idempotenzschlüssel auf, wenn die Arbeit pro Shop unabhängig ist. Eine globale Sperre kann nicht zusammenhängende Arbeit unnötig serialisieren, während ein Schlüssel ohne Zielidentität zwei unterschiedliche Exporte als Duplikate behandeln kann.

Geben Sie Cachedaten dieselben Dimensionen wie der Abfrage

Ein Cacheschlüssel ist Teil des Datenmodells. Wenn ein Ergebnis von Shop, Sprache, Währung oder Kundengruppe abhängt, müssen Schlüssel und Invalidierungsstrategie die relevanten Dimensionen widerspiegeln.

Bei einer moduleigenen Produktbezeichnung könnte ein konzeptioneller Schlüssel Modulnamespace, Produkt-ID, Shop-ID, Sprach-ID und eine Darstellungsversion enthalten. Dies ist ein Entwurfsmuster, kein vorgeschriebenes PrestaShop-Cacheschlüsselformat.

Nehmen Sie nur Dimensionen auf, die den Wert tatsächlich beeinflussen, lassen Sie aber keine aus Bequemlichkeit weg. Eine korrekte Datenbankabfrage mit anschließend gemeinsam genutztem Cacheeintrag kann das Ergebnis des ersten Shops weiterhin in den zweiten durchreichen.

Testen Sie dieselbe Entität in unterschiedlichen Kontexten in beiden Reihenfolgen: A dann B sowie B dann A. Wenn das Ergebnis mit der Anfragereihenfolge wechselt, vermuten Sie gecachten oder beibehaltenen Kontext. Tests ausschließlich mit geleertem Cache können diese Fehlerklasse verbergen.

Die Invalidierung muss jede betroffene Darstellung erreichen. Eine globale Wertänderung kann die Invalidierung mehrerer shopspezifischer Ausgaben erfordern; eine lokale Änderung darf beim Cache-Neuaufbau keine Daten anderer Shops überschreiben.

Verwenden Sie widersprüchliche Testdatensätze für Isolationstests

Erstellen Sie zwei Shops mit bewusst widersprüchlichen Werten für eine gemeinsame Entität. Geben Sie ihnen unterschiedliche externe Zielkennungen und verschiedene geerbte/lokale Konfigurationszustände. Prüfen Sie anschließend jeden Lese- und Schreibvorgang über die unterstützten Modulschnittstellen.

Nützliche Prüfbedingungen sind:

  • Ein Schreibvorgang in Shop A lässt Zeile und effektiven Wert von Shop B unverändert.
  • Ein Lesevorgang in A greift ohne ausdrückliche Geschäftsregel niemals auf B zurück.
  • Eine Aktion für alle Shops betrifft genau die ausgewählte autorisierte Menge.
  • Ein Hintergrundjob verarbeitet den Shop, der in seinen Nutzdaten genannt wird.
  • Cacheergebnisse bleiben unabhängig von der Anfragereihenfolge korrekt.
  • Doppelte gleichzeitige Schreibvorgänge erhalten die angegebene eindeutige Identität.
  • Das Entfernen einer lokalen Überschreibung stellt Vererbung wieder her, statt einen übergeordneten Wert zu kopieren.

Testen Sie auch fehlende Zuordnungen und gelöschte Entitäten. Das Modul sollte einen definierten Fehler oder ein definiertes Fehlen zurückgeben, statt eine beliebige verbleibende Zeile auszuwählen, um den Ablauf fortzusetzen.

Bewahren Sie für einen Regressionsbericht die beiden Shop-IDs, die unterschiedlichen Testwerte und die Operation auf, die die Grenze überschritten hat. Ein minimaler Zwei-Shop-Fall erklärt den Defekt häufig klarer als ein großer Produktionsexport.

Machen Sie Isolation zur Gewohnheit bei Reviews

Fragen Sie beim Code-Review, wo der Bereich in den Dienst gelangt, wo er validiert wird und ob jede Persistenz- und Cacheoperation ihn erhält. Prüfen Sie Abfrageprädikate und Eindeutigkeitseinschränkungen gemeinsam. Prüfen Sie Hintergrundnachrichtendaten zusammen mit den Handlern, die sie verarbeiten.

Beginnen Sie bei bestehenden Modulen mit Schreibvorgängen mit den größten Auswirkungen und externen Integrationen. Einen eingegrenzten Lesevorgang zu korrigieren reicht nicht, wenn die zugehörige Aktualisierung oder der Export weiterhin globale Standardwerte verwendet.

Multistore-Korrektheit ist kein besonderer Modus, der am Entwicklungsende ergänzt wird. Sie ist die Übereinstimmung zwischen der Bedeutung der Daten und jeder Grenze, die sie verarbeitet. Sobald diese Übereinstimmung ausdrücklich ist, werden shopübergreifende Lecks zu testbaren Fehlern statt zu rätselhaften Kundenmeldungen.

Zum Kontext der zugrunde liegenden Plattform lesen Sie unseren Leitfaden zur Modulentwicklung für PrestaShop 9 und wenden Sie die obigen versionsspezifischen Prüfungen auf die bewertete Installation an.

Bei der Auswahl einer Erweiterung sehen Sie sich die PrestaShop-9-Module von PrestaSuite an und gleichen Sie die dokumentierte Versionsunterstützung und die Integrationsanforderungen mit Ihren eigenen Testergebnissen ab.