Mehrere separate Caches wegen dynamischer Inhalte

Hallo,

ich versuche momentan, Shopware (Community Edition) dazu zu bewegen, abhängig von einem Session-Wert verschiedene Caches (primär geht es hier um den Template-Cache) zu nutzen. Das Szenario bei uns stellt sich nämlich so dar: Wir haben mehrere Partner, für die wir teilweise abweichende Logiken und Darstellungen verwenden. Z.B. sollen nach einem bestimmten Einstieg in den Shop andere Konditionen gelten oder z.B. eine andere Service-Telefonnummer angezeigt werden. Wir nutzen die Session-Variable sPartner zur Unterscheidung. Der Shop hat nun aber offenbar Probleme, mehrere Versionen einer Seite mit identischer URL separat zu cachen.

Ich habe vieles versucht, inkl. beim Predispatch-Event im View-Objekt abhängig vom Session-Parameter die Cache-ID und Compile-ID zu modifizieren (ist es da möglicherweise schon zu spät?). Zum Zeitpunkt, an dem die config.php geladen wird, steht zudem die Session noch nicht zur Verfügung. Aber auch Experimente, dann stattdessen einen Cookie zu verwenden, schlugen fehl. Das Setzen von “‘template’ => array( ‘cacheDir’ => ‘…’, ‘compileDir’ => ‘…’)” in der config.php hatte seltsamerweise auch keinen Effekt bzw. ich habe die Befürchtung, dass die Datei dann auch nur einmal beim ersten Mal ausgewertet wird und ab da dann auch gecacht wird, also keine dynamische Auswertung möglich ist. Auch das Manipulieren der ENV-Umgebungsvariable mit mehreren Config-Dateien brachte keinen Erfolg.

Hat also vielleicht noch jemand eine andere Idee, wie man abhängig von einem Session-Wert unterschiedliche (Template-)Caches nutzen kann? Vielleicht gibt es auch einen ganz anderen Ansatz? Ich möchte übrigens ungern die relevanten Bereiche im Template pauschal als “nocache” markieren. Der Cache sollte schon vollumfänglich greifen, nur eben in mehreren Ausführungen abhängig vom Partner.

Evtl kann man das mit Subshops oder Sprachen lösen?

Oder die Logik dafür kann man aus diesen Komponenten extrahieren?

Hi,

wie sehen denn deine Template-Erweiterungen aus? Normalerweise empfehle ich bei sowas immer, das Template so zu bauen, dass es basierend auf einer View-Variable bspw. unterschiedliche DIVs ausgibt. Was eben wegen des Compile-Caches immer sehr schwierig ist, ist es bspw. unterschiedliche Templates einzubinden. Wenn du den “Switch” also direkt in das Template schieben kannst, ist das oft schon eine mögliche Lösung.

Hast du den HTTP-Cache aus? Ansonsten müsste man den ja auch noch beachten.

Daniel

Also wir haben uns nun geschlagen gegeben und es über einen Fix am Core gelöst. Eigentlich hätte es über Events funktionieren müssen, aber aus unerklärlichen Gründen wurde die Änderung der Compile-ID nur akzeptiert, wenn man sie direkt in der Shop.php macht, anstatt unmittelbar nach Ausführung der Methode in der Shop.php diese zu modifizieren. Die Idee war dann, dass der Wert vielleicht nur beim erstmaligen Setzen genommen wird, aber auch ein anderer Ansatz, die Compile-ID vorher zu setzen, trug keine Früchte. Dabei waren alle Tests was das Ändern der Compile-ID betrifft erfolgreich. Ich hatte sogar einen zweiten Eventlistener eingebaut, um den Wert nach der Änderung quasi unabhängig auszulesen, und es wurde korrekt die geänderte Compile-ID vom Template-Manager zurückgeliefert. Funktioniert hat es aber dennoch nicht.

Das Event- und Hook-System in Ehren, wenn es funktioniert, macht es wirklich Spaß, aber solche Fälle sind auch keine Einzelfälle. Zum einen kann ich es auch oft nicht anders nennen als Shopware verarschen zu müssen, und dann lässt sich Shopware noch nicht mal verarschen.

Hi,

naja, auf das “wie wir es vorschlagen” bin ich ja oben eingegangen, das hast du jetzt leider komplett übergangen. Kann ja sein, dass das für euch nicht passt - das wäre dann auch gutes Feedback für uns. Das “setCompileId()” auf dem Template nutzen wir ja im Core auch an einigen Stellen, bspw. um andere Compile-Caches für Secure-Requests und Requests mit ESI-Unterstützung zu generieren. Von daher sollte™ das prinzipiell schon funktionieren. Aber lässt sich aus der Ferne natürlich auch nur bedingt beurteilen :slight_smile:

Daniel

Also alles in einem Template zu lösen mit If-Bedingungen kommt nicht wirklich infrage. Wir nutzen ein eigenes Template (Shopware-4-Legacy, basierend auf Emotion, aber quasi von Grund auf neu gebaut) und leiten für die einzelnen sPartner dann nochmals davon ab, indem es dann zu “emotion_haupttheme” noch “emotion_haupttheme_partner1”, “emotion_haupttheme_partner2” usw. gibt (die heißen tatsächlich anders, aber das System wird deutlich). Das wird ermöglicht durch Injizieren einer weiteren TemplateDir-Ebene an den Anfang des Arrays in der Smarty-View. Auf die Weise haben wir ein Haupttheme für den Standardshop und können dann beliebige Änderungen gekapselt für die einzelnen Partner vornehmen und dabei die gewohnte Ableitungsmethodik nutzen.

Ich kann ja mal schildern, was ich eigentlich als Lösungsweg erarbeitet hatte, um die Compile-ID zu erweitern. Das Ziel war wie gesagt, in Datei “engine/Shopware/Models/Shop/Shop.php” in Methode “registerResources” unmittelbar nach Aufruf von “$templateManager->setCompileId()” einzuhaken und die gesetzte Compile-ID um die Partnerkennung zu erweitern. Es direkt in der Datei zu machen, hat funktioniert. Nun musste das “nur” noch über ein Plug-in ohne Core-Modifizierung klappen. Dazu habe ich den Event “Enlight_Controller_Front_RouteStartup” genutzt, der von der Bootstrap.php des Router-Core-Plug-ins in Methode “onRouteStartup” nutzt wird und am Ende jene Methode “registerResources” aufruft. Da der Eventlistener dort mit keiner Priorität registriert wird, also auf 0 defaultet, habe ich für den eigenen Listener dann die Priorität 1 genommen, damit mein Plug-in unmittelbar nach Aufruf der eigentlichen Methode folgt. Wenn man sich den Quellcode anschaut, dann sieht man, dass registerResources in der Bootstrap.php ganz am Ende aufgerufen wird, und die Compile-ID in der Shop-Methode auch ziemlich am Schluss gesetzt wird. Es dürfte also nach meinem Verständnis kein Code die ursprüngliche Compile-ID verarbeiten ehe ich über das Plug-in eine Änderung daran vorgenommen habe. Ein Test mit einem zweiten identischen Listener mit Priorität 2 bestätigt zudem, dass die Compile-ID erfolgreich verändert wurde und auch über mehrere Methodenaufrufe hinweg erhalten bleibt. Daher würde ich eigentlich fest davon ausgehen, dass es auf diese Weise dann funktionieren müsste, nahezu so wie wenn die Compile-ID direkt im Core verändert würde. Nur leider klappt es nicht, aus mir schleierhaften Gründen.

Hi,

ok, verstehe. Das onRouteStartup ist zum Modifizieren ggf. etwas tricky, weil der Router im onRouteShutdown ja ggf. in die \Shopware_Plugins_Core_Router_Bootstrap::upgradeShop läuft und dort nochmal die registerRessources auf dem Shop aufruft. Dabei wird die compileId dann jeweils überschrieben:

            $templateManager->setCompileId(
                'frontend' .
                    '_' . $template->toString() .
                    '_' . $localeName .
                    '_' . $this->getId()
            );

Das würde das von dir beschriebene Verhalten erklären. Da sind noch einige Bedingungen, wo er vorher rausspringen könnte - aber prinzipiell würde ich auf Grund des SourceCodes denken, das das onRouteShutdown-Event zur Erweiterung ggf. geeigneter ist, oder? 

Besten Gruß,

Daniel

Hmm, das wäre vielleicht noch einen Versuch wert. Sofern tatsächlich mehrfach die selbe registerResources-Methode an der Shop-Klasse aufgerufen wird, ich aber nur nach dem ersten Mal eingreife, würde das das Verhalten erklären. Wobei es ja etwas ineffizient ist, das mehrmals zu machen und ich daher nicht damit gerechnet habe, dass das möglicherweise passiert. Danke für den Tipp. Das könnte vielleicht schon die Lösung sein. Werde berichten.

Hi,

bei der registerRessource geht es ja letztlich um den Shop-Context. Und dieser Context verändert sich im Laufe des Routings unter Umständen - von daher ist das da so gelöst. Aus ähnlichen Gründen kann registerResource durchaus auch aus dem Backend oder anderen Routen ausgelöst werden, wenn ein anderer Shop-Context erzeugt wird. Wenn ich das richtig sehe, kannst du dein setCompileId() auch noch später im Stack auslösen, der ViewRenderer greift erst im globalen PostDispatch-Event. 

Aber sag mal auf jeden Fall Bescheid  Thumb-Up

Daniel

1 „Gefällt mir“

Der Tipp hat geholfen, danke. Smile

Ich verwende jetzt diesen Event-Listener:

$this->subscribeEvent(
	'Enlight_Controller_Action_Init',
	'onEnlightControllerActionInit',
	401
);

Die 401 sorgt dafür, dass der Event erst nach dem Initialisieren der View erfolgt. Dann besteht darauf auch direkt Zugriff und es können Variablen zugewiesen werden, was in unserem Plug-in auch von Vorteil ist.