Me he encontrado con un problema en el que un bloque que debería ser único por página no es para usuarios desconectados. El problema es un complemento de bloque personalizado que tengo en una página de búsqueda de vistas que contiene filtros personalizados (algo así como un reemplazo personalizado para filtros expuestos. El bloque se coloca a través de / admin / structure / block).
Según lo que aprendí sobre Drupal 8, agregué los contextos de caché a mi matriz de compilación:
public function build() {
$search_form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\SearchForm');
return [
'search_form' => $search_form,
'#cache' => ['contexts' => ['url.path', 'url.query_args']]
];
}
Pero parece que esto debe ser incorrecto porque cuando se cierra la sesión, el bloque se almacena en caché en la primera vista, y cuando la URL cambia, no se muestra una nueva versión del bloque.
Pensé que podría ser la página de vista la que estaba causando el problema, pero incluso cuando desactivé el almacenamiento en caché en la página de vista, el problema persistió.
Pude solucionar el problema de varias maneras, por ejemplo, usando un gancho preprocess_block:
function mymodule_preprocess_block__mycustomsearchblock(&$variables) {
$variables['#cache']['contexts'][] = 'url.path';
$variables['#cache']['contexts'][] = 'url.query_args';
}
Pero me molestó que no pudiera simplemente poner los contextos de caché en la matriz de compilación de mi bloque.
Dado que mi bloque extiende BlockBase, decidí probar el método getCacheContexts (), especialmente porque vi que algunos módulos dentro del núcleo lo están haciendo de esta manera.
public function getCacheContexts() {
return Cache::mergeContexts(parent::getCacheContexts(), ['url.path', 'url.query_args']);
}
Esto también soluciona el problema, pero curiosamente, cuando imprimo las variables en la función de bloque de preproceso, estas no se muestran en $ variables ['# caché'] ['contextos'], pero sí se muestran en $ variables ['elementos '] [' # caché '] [' contextos ']
array:5 [▼
0 => "languages:language_interface"
1 => "theme"
2 => "url.path"
3 => "url.query_args"
4 => "user.permissions"
]
Estoy tratando de descubrir cómo funciona esto y por qué no funcionaba desde la función de compilación.
Al mirar /core/modules/block/src/BlockViewBuilder.php en la función viewMultiple (), parece que extrae las etiquetas de caché de la entidad y el complemento:
'contexts' => Cache::mergeContexts(
$entity->getCacheContexts(),
$plugin->getCacheContexts()
),
Eso explica por qué agregar un método getCacheContexts () a mi complemento de bloque agrega los contextos a mi bloque. Además, al observar el método preRender en la misma clase, parece que no usa la matriz de caché en la función de construcción de bloques, lo que me confunde, ya que parece que la forma de agregar el almacenamiento en caché en Drupal 8 es agregar un #caché elemento para representar elementos.
Entonces mi pregunta es,
1) ¿Se ignoran los contextos de caché agregados directamente en la matriz en un complemento de bloque?
2) Si es así, ¿hay alguna forma de evitarlo? ¿Necesitamos agregarlo a un elemento hijo de la matriz de compilación?
3) Si se ignora el contexto agregado directamente, ¿agregar el getCacheContexts () es el camino a seguir para los complementos de bloque en módulos personalizados?
Respuestas:
En la mayoría de los casos, simplemente configura el contexto de caché directamente en la matriz de renderizado que devuelve en su método build ().
Finalmente encontré cuál era mi problema, con la ayuda de @Berdir y @ 4k4. Si está utilizando una plantilla personalizada, como block - myblock.html.twig y genera las variables individualmente, como {{content.foo}} en lugar de todas al mismo tiempo como {{content}}, ignora sus contextos de caché pasaron directamente a su conjunto de compilación de bloques, cuando se cerró la sesión. Consulte ¿Cuál es la forma correcta de establecer contextos de caché en bloques personalizados?
Entonces, para responder la pregunta original:
1) Los contextos de caché pasados directamente a un complemento de bloque personalizado a veces se ignoran. Puede probar esto alterando el SyndicateBlock y luego creando una plantilla personalizada en su bloque de tema: syndicate.html.php en el que genera las variables individualmente de esta manera:
A medida que cambie los argumentos de la URL, verá que el bloque no respeta el contexto de caché.
Ahora, si lo cambia, genera todo el contenido como una pieza, funciona:
Ahora, respeta el contexto de caché, y el bloque es único por página.
2) Por ahora, para evitar esto, solo puede generar lo que está en su bloque en su propia plantilla.
Esto evita las excepciones de almacenamiento en caché esotérico del módulo de bloque y su formulario ahora es único por página cuando se cierra la sesión.
3) ¿Debería crear su propia plantilla de tema para solucionar esto, o simplemente agregar un método para getCacheContexts () en su complemento Block personalizado? Es mejor crear una nueva plantilla de tema, en lugar de agregar un método getCacheContexts () que anula el orden natural de los contextos de caché y puede romper los metadatos más profundamente en su matriz de compilación.
fuente
Para cualquiera que encuentre esto ...
La razón por la que la representación
content
(ocontent|without()
) funciona es que hay un elemento en la matriz de representacióncontent['#cache']
que contiene todos los metadatos almacenables en caché para el contenido.Si no permite que esto se represente en ramita,
content
o bien{{'#cache': content['#cache']|render }}
, la página no sabe que tiene metadatos almacenables en caché (por ejemplo, nunca aparece).Sin embargo, parece que no estás haciendo una rama personalizada. Si está usando algo como Varnish, eso también podría ser un culpable para los usuarios anónimos.
fuente
También me encontré con este problema y la solución que se me ocurrió fue crear una nueva variable block_content basada en una versión filtrada de la variable de contenido principal, excluyendo cualquier campo personalizado que desee representar manualmente:
Luego, en lugar de representar la variable "content.body" directamente más tarde, llamo:
Si desea renderizar cada campo individualmente, puede seguir agregándolos al filtro "sin" para que la representación block_content no haga nada más que arreglar el almacenamiento en caché.
fuente
El método más fácil para lograr esto es declarando y definiendo el
getCacheContexts()
métodoConsulte la documentación de CacheableDependency , debe contener todo lo que necesita;)
fuente