Ocorreu um problema em que um bloco que deveria ser único por página não é para usuários desconectados. O problema é um plug-in de bloco personalizado que tenho em uma página de pesquisa de visualizações que contém filtros personalizados (como uma substituição personalizada de filtros expostos. O bloco colocado por meio de / admin / structure / block).
Com base no que aprendi sobre o Drupal 8, adicionei os contextos de cache à minha matriz de compilação:
public function build() {
$search_form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\SearchForm');
return [
'search_form' => $search_form,
'#cache' => ['contexts' => ['url.path', 'url.query_args']]
];
}
Mas parece que isso deve estar incorreto porque, quando desconectado, o bloco é armazenado em cache na primeira visualização e, quando o URL é alterado, ele não mostra uma nova versão do bloco.
Eu pensei que poderia ser a página de visualização que estava causando o problema, mas mesmo quando desativei o cache na página de visualização, o problema permaneceu.
Consegui corrigir o problema de várias maneiras, por exemplo, usando um gancho preprocess_block:
function mymodule_preprocess_block__mycustomsearchblock(&$variables) {
$variables['#cache']['contexts'][] = 'url.path';
$variables['#cache']['contexts'][] = 'url.query_args';
}
Mas me incomodou que eu não pudesse simplesmente colocar os contextos de cache na matriz de compilação do meu bloco.
Como meu bloco estende o BlockBase, decidi experimentar o método getCacheContexts (), especialmente porque vi que alguns módulos no núcleo estão fazendo dessa maneira.
public function getCacheContexts() {
return Cache::mergeContexts(parent::getCacheContexts(), ['url.path', 'url.query_args']);
}
Isso também corrigiu o problema, mas, curiosamente, quando eu mostro as variáveis na função de bloco de pré-processo, elas não são exibidas em $ variable ['# cache'] ['contexts'], mas são exibidas nos elementos $ variable [' '] [' # cache '] [' contextos ']
array:5 [▼
0 => "languages:language_interface"
1 => "theme"
2 => "url.path"
3 => "url.query_args"
4 => "user.permissions"
]
Estou tentando descobrir como isso funciona e por que não estava funcionando na função de compilação.
Olhando para /core/modules/block/src/BlockViewBuilder.php na função viewMultiple (), parece que ele extrai as tags de cache da entidade e do plug-in:
'contexts' => Cache::mergeContexts(
$entity->getCacheContexts(),
$plugin->getCacheContexts()
),
Então, isso explica por que adicionar um método getCacheContexts () ao meu plug-in de bloco adiciona os contextos ao meu bloco. Além disso, olhando para o método preRender na mesma classe, parece que ele não usa a matriz de cache na função de construção de blocos, o que me confunde, pois parece que a maneira de adicionar cache no Drupal 8 é adicionar um #cache elemento para renderizar elementos.
Então, minha pergunta é:
1) Os contextos de cache adicionados diretamente à matriz em um plug-in de bloco são ignorados?
2) Em caso afirmativo, existe uma maneira de contornar isso, precisamos adicioná-lo a um elemento filho da matriz de compilação?
3) Se o contexto adicionado diretamente for ignorado, a adição de um getCacheContexts () é o caminho a seguir para plug-ins de bloco em módulos personalizados?
Respostas:
Na maioria dos casos, você apenas define o contexto do cache diretamente na matriz de renderização que retorna em seu método build ().
Finalmente descobri qual era o meu problema, com a ajuda de @Berdir e @ 4k4. Se você estiver usando um modelo personalizado, como block - myblock.html.twig, e gerar as variáveis individualmente, como {{content.foo}}, em vez de todas ao mesmo tempo como {{content}}, ele ignorará seus contextos de cache passados diretamente para sua matriz de criação de blocos, quando desconectados. Consulte Qual é a maneira correta de definir contextos de cache em blocos personalizados?
Então, para responder à pergunta original:
1) Contextos de cache passados diretamente para um plug-in de bloco personalizado às vezes são ignorados. Você pode testar isso alterando o SyndicateBlock e, em seguida, criando um modelo personalizado no seu bloco de temas - syndicate.html.php, no qual você gera as variáveis individualmente assim:
À medida que você altera os argumentos do URL, verá que o bloco não respeita o contexto do cache.
Agora, se você alterá-lo, produzindo todo o conteúdo como uma peça, funciona:
Agora, ele respeita o contexto do cache e o bloco é único por página.
2) Por enquanto, para contornar isso, você pode simplesmente exibir o que está no seu bloco no seu próprio modelo.
Isso contorna as exceções de cache esotérico do módulo de bloco e seu formulário agora é único por página quando desconectado.
3) Você deve criar seu próprio modelo de tema para corrigir isso ou simplesmente adicionar um método para getCacheContexts () no seu plug-in de bloco personalizado? É melhor criar um novo modelo de tema, em vez de adicionar um método getCacheContexts () que substitua a ordem natural de integração de contextos de cache e pode quebrar os metadados mais profundamente em sua matriz de compilação.
fonte
Para quem encontrar isso ...
O motivo pelo qual a renderização
content
(oucontent|without()
) funciona é que existe um elemento na matriz de renderizaçãocontent['#cache']
que contém todos os metadados armazenáveis em cache do conteúdo.Se você não permitir que isso seja renderizado em galho,
content
ou{{'#cache': content['#cache']|render }}
a página não saberá que possui metadados armazenáveis em cache (por exemplo, nunca borbulha).Parece que você não está fazendo um galho personalizado. Se você estiver usando algo como Varnish, isso também pode ser um culpado para usuários anônimos.
fonte
Também deparei com esse problema e a solução alternativa que criei foi criar uma nova variável block_content com base em uma versão filtrada da variável de conteúdo principal, excluindo os campos personalizados que desejo processar manualmente:
Então, em vez de renderizar a variável "content.body" diretamente mais tarde, eu chamo:
Se você deseja renderizar todos os campos individualmente, basta continuar adicionando-os ao filtro "sem" para que a renderização block_content não faça nada além de corrigir o cache.
fonte
O método mais fácil de conseguir isso é declarando e definindo o
getCacheContexts()
métodoConfira a documentação do CacheableDependency , que deve conter tudo o que você precisa;)
fonte