Ich bin auf ein Problem gestoßen, bei dem ein Block, der pro Seite eindeutig sein sollte, nicht für abgemeldete Benutzer geeignet ist. Das Problem ist ein benutzerdefiniertes Block-Plugin, das ich auf einer Ansichtssuchseite habe und das benutzerdefinierte Filter enthält (ähnlich wie ein benutzerdefinierter Ersatz für exponierte Filter. Der Block wird durch / admin / structure / block platziert).
Basierend auf dem, was ich über Drupal 8 gelernt habe, habe ich die Cache-Kontexte zu meinem Build-Array hinzugefügt:
public function build() {
$search_form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\SearchForm');
return [
'search_form' => $search_form,
'#cache' => ['contexts' => ['url.path', 'url.query_args']]
];
}
Dies muss jedoch falsch sein, da der Block beim Abmelden bei der ersten Ansicht zwischengespeichert wird und bei Änderung der URL keine neue Version des Blocks angezeigt wird.
Ich dachte, es könnte die Ansichtsseite sein, die das Problem verursacht hat, aber selbst als ich das Caching auf der Ansichtsseite deaktivierte, blieb das Problem bestehen.
Ich konnte das Problem auf verschiedene Arten beheben, beispielsweise mithilfe eines preprocess_block-Hooks:
function mymodule_preprocess_block__mycustomsearchblock(&$variables) {
$variables['#cache']['contexts'][] = 'url.path';
$variables['#cache']['contexts'][] = 'url.query_args';
}
Aber es störte mich, dass ich die Cache-Kontexte nicht einfach in das Build-Array meines Blocks einfügen konnte.
Da mein Block BlockBase erweitert, habe ich beschlossen, die Methode getCacheContexts () auszuprobieren, insbesondere weil ich gesehen habe, dass einige Module im Kern dies auf diese Weise tun.
public function getCacheContexts() {
return Cache::mergeContexts(parent::getCacheContexts(), ['url.path', 'url.query_args']);
}
Dies hat auch das Problem behoben, aber interessanterweise werden diese, wenn ich die Variablen in der Vorverarbeitungsblockfunktion ausgeben, nicht in $ variables ['# cache'] ['context'] angezeigt, sondern in $ variables ['Elementen '] [' # cache '] [' context ']
array:5 [▼
0 => "languages:language_interface"
1 => "theme"
2 => "url.path"
3 => "url.query_args"
4 => "user.permissions"
]
Ich versuche herauszufinden, wie dies funktioniert und warum es mit der Build-Funktion nicht funktioniert hat.
Wenn Sie sich /core/modules/block/src/BlockViewBuilder.php in der Funktion viewMultiple () ansehen, sieht es so aus, als würden die Cache-Tags aus der Entität und dem Plugin abgerufen:
'contexts' => Cache::mergeContexts(
$entity->getCacheContexts(),
$plugin->getCacheContexts()
),
Das erklärt, warum das Hinzufügen einer getCacheContexts () -Methode zu meinem Block-Plugin die Kontexte zu meinem Block hinzufügt. Wenn man sich die preRender-Methode in derselben Klasse ansieht, sieht es so aus, als würde das Cache-Array in der Blockerstellungsfunktion nicht verwendet, was mich verwirrt, da das Hinzufügen von Caching in Drupal 8 anscheinend darin besteht, einen #cache hinzuzufügen Element zum Rendern von Elementen.
Meine Frage ist also:
1) Werden Cache-Kontexte, die direkt zum Array in einem Block-Plugin hinzugefügt wurden, ignoriert?
2) Wenn ja, gibt es einen Weg, dies zu umgehen, müssen wir es einem untergeordneten Element des Build-Arrays hinzufügen?
3) Wenn der direkt hinzugefügte Kontext ignoriert wird, ist das Hinzufügen von getCacheContexts () der richtige Weg, um Block-Plugins in benutzerdefinierten Modulen zu verwenden?
Antworten:
In den meisten Fällen legen Sie den Cache-Kontext einfach direkt auf dem Render-Array fest, das Sie in Ihrer build () -Methode zurückgeben.
Mit Hilfe von @Berdir und @ 4k4 habe ich endlich herausgefunden, was mein Problem war. Wenn Sie eine benutzerdefinierte Vorlage wie block - myblock.html.twig verwenden und die Variablen einzeln ausgeben, z. B. {{content.foo}}, anstatt alle gleichzeitig wie {{content}}, werden sie ignoriert Ihre Cache-Kontexte werden beim Abmelden direkt an Ihr Blockbuild-Array übergeben. Siehe Was ist der richtige Weg, um Cache-Kontexte für benutzerdefinierte Blöcke festzulegen?
Um die ursprüngliche Frage zu beantworten:
1) Cache-Kontexte, die direkt an ein benutzerdefiniertes Block-Plugin übergeben werden, werden manchmal ignoriert. Sie können dies testen, indem Sie den SyndicateBlock ändern und dann eine benutzerdefinierte Vorlage in Ihrem Themenblock erstellen - syndicate.html.php, in der Sie die Variablen einzeln wie folgt ausgeben:
Wenn Sie die URL-Argumente ändern, wird der Block den Cache-Kontext nicht berücksichtigen.
Wenn Sie nun den gesamten Inhalt als Stück ausgeben, funktioniert es:
Jetzt wird der Cache-Kontext berücksichtigt, und der Block ist pro Seite eindeutig.
2) Um dies zu umgehen, können Sie zunächst nur das, was sich in Ihrem Block befindet, in einer eigenen Vorlage ausgeben.
Dadurch werden die esoterischen Caching-Ausnahmen des Blockmoduls umgangen, und Ihr Formular ist jetzt beim Abmelden pro Seite eindeutig.
3) Sollten Sie eine eigene Designvorlage erstellen, um dies zu beheben, oder einfach eine Methode für getCacheContexts () in Ihrem benutzerdefinierten Block-Plugin hinzufügen? Es ist besser, eine neue Designvorlage zu erstellen, als eine getCacheContexts () -Methode hinzuzufügen, die die natürliche Reihenfolge des Aufsprudelns von Cache-Kontexten überschreibt und möglicherweise Metadaten tiefer in Ihrem Build-Array aufbricht.
quelle
Für alle anderen, die dies finden ...
Der Grund, warum das Rendern
content
(odercontent|without()
) funktioniert, ist, dass das Render-Array ein Elementcontent['#cache']
enthält, das alle zwischenspeicherbaren Metadaten für den Inhalt enthält.Wenn Sie nicht zulassen, dass dies im Zweig gerendert wird,
content
oder{{'#cache': content['#cache']|render }}
die Seite nicht weiß, dass sie zwischenspeicherbare Metadaten enthält (z. B. sprudelt sie nie).Klingt so, als ob Sie keinen benutzerdefinierten Zweig machen. Wenn Sie etwas wie Lack verwenden, kann dies auch ein Schuldiger für anonyme Benutzer sein.
quelle
Ich bin auch auf dieses Problem gestoßen. Die Problemumgehung bestand darin, eine neue Variable block_content zu erstellen, die auf einer gefilterten Version der Hauptinhaltsvariablen basiert, mit Ausnahme aller benutzerdefinierten Felder, die ich manuell rendern möchte:
Anstatt die Variable "content.body" direkt später zu rendern, rufe ich auf:
Wenn Sie jedes Feld einzeln rendern möchten, können Sie es einfach weiter zum Filter "ohne" hinzufügen, damit beim Rendern von block_content nur das Caching korrigiert wird.
quelle
Die einfachere Methode, um dies zu erreichen, besteht darin, die
getCacheContexts()
Methode zu deklarieren und zu definierenSchauen Sie sich die CacheableDependency- Dokumentation an, sie sollte alles enthalten, was Sie brauchen;)
quelle