Wystąpił problem polegający na tym, że blok, który powinien być unikalny na stronie, nie jest dla wylogowanych użytkowników. Problemem jest niestandardowa wtyczka blokowa, którą mam na stronie wyszukiwania widoków, która zawiera niestandardowe filtry (coś w rodzaju niestandardowego zamiennika dla odsłoniętych filtrów. Blok umieszczony przez / admin / structure / block).
Na podstawie tego, czego dowiedziałem się o Drupal 8, dodałem konteksty pamięci podręcznej do mojej tablicy kompilacji:
public function build() {
$search_form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\SearchForm');
return [
'search_form' => $search_form,
'#cache' => ['contexts' => ['url.path', 'url.query_args']]
];
}
Wygląda jednak na to, że musi to być niepoprawne, ponieważ po wylogowaniu blok zostanie zapisany w pamięci podręcznej przy pierwszym widoku, a gdy zmieni się adres URL, nie będzie pokazywał nowej wersji bloku.
Myślałem, że przyczyną może być strona widoku, ale nawet po wyłączeniu buforowania na stronie widoku problem pozostał.
Byłem w stanie rozwiązać problem na kilka sposobów, na przykład za pomocą haka preprocess_block:
function mymodule_preprocess_block__mycustomsearchblock(&$variables) {
$variables['#cache']['contexts'][] = 'url.path';
$variables['#cache']['contexts'][] = 'url.query_args';
}
Ale przeszkadzało mi to, że nie mogłem po prostu umieścić kontekstów pamięci podręcznej w tablicy kompilacji mojego bloku.
Ponieważ mój blok rozszerza BlockBase, postanowiłem wypróbować metodę getCacheContexts (), zwłaszcza że widziałem, że niektóre moduły w rdzeniu robią to w ten sposób.
public function getCacheContexts() {
return Cache::mergeContexts(parent::getCacheContexts(), ['url.path', 'url.query_args']);
}
To również rozwiązało problem, ale co ciekawe, kiedy wypisuję zmienne w funkcji bloku przetwarzania wstępnego, nie są one wyświetlane w $ zmiennych ['# cache'] ['kontekstach'], ale pokazują się w elementach $ zmiennych [' '] [' # cache '] [' konteksty ']
array:5 [▼
0 => "languages:language_interface"
1 => "theme"
2 => "url.path"
3 => "url.query_args"
4 => "user.permissions"
]
Próbuję dowiedzieć się, jak to działa i dlaczego nie działa z funkcji kompilacji.
Patrząc na /core/modules/block/src/BlockViewBuilder.php w funkcji viewMultiple (), wygląda na to, że pobiera tagi pamięci podręcznej z encji i wtyczki:
'contexts' => Cache::mergeContexts(
$entity->getCacheContexts(),
$plugin->getCacheContexts()
),
To wyjaśnia, dlaczego dodanie metody getCacheContexts () do mojej wtyczki bloku dodaje konteksty do mojego bloku. Patrząc na metodę preRender w tej samej klasie, wygląda na to, że nie używa ona tablicy pamięci podręcznej w funkcji budowania bloków, co mnie dezorientuje, ponieważ wydaje się, że sposobem na dodanie buforowania w Drupal 8 jest dodanie pamięci podręcznej # element do renderowania elementów.
Więc moje pytanie brzmi:
1) Czy konteksty pamięci podręcznej dodawane bezpośrednio do tablicy w wtyczce bloku są ignorowane?
2) Jeśli tak, to czy można to obejść, czy musimy dodać go do elementu potomnego tablicy kompilacji?
3) Jeśli kontekst dodany bezpośrednio jest ignorowany, czy dodanie getCacheContexts () jest sposobem na wtyczki blokowe w niestandardowych modułach?
Odpowiedzi:
W większości przypadków kontekst pamięci podręcznej jest ustawiany bezpośrednio na tablicy renderowania zwracanej w metodzie build ().
W końcu odkryłem, na czym polegał mój problem, przy pomocy @Berdir i @ 4k4. Jeśli używasz niestandardowego szablonu, takiego jak blok - myblock.html.twig, i wyprowadzasz zmienne osobno, takie jak {{content.foo}} zamiast wszystkich w tym samym czasie jak {{content}}, ignoruje konteksty pamięci podręcznej przekazywane po wylogowaniu bezpośrednio do tablicy kompilacji bloku. Zobacz Jaki jest prawidłowy sposób ustawiania kontekstów pamięci podręcznej w niestandardowych blokach?
Aby odpowiedzieć na pierwotne pytanie:
1) Konteksty pamięci podręcznej przekazywane bezpośrednio do niestandardowej wtyczki blokowej są czasami ignorowane. Możesz to przetestować, zmieniając SyndicateBlock , a następnie tworząc niestandardowy szablon w bloku motywu - syndicate.html.php, w którym wypisujesz zmienne indywidualnie w następujący sposób:
Podczas zmiany argumentów adresu URL zobaczysz, że blok nie szanuje kontekstu pamięci podręcznej.
Teraz, jeśli zmienisz, wypisuje całą zawartość jako kawałek, działa:
Teraz szanuje kontekst pamięci podręcznej, a blok jest unikalny dla każdej strony.
2) Na razie, aby obejść ten problem, możesz po prostu wypisać zawartość bloku w swoim własnym szablonie.
To omija ezoteryczne wyjątki buforowania modułu blokowego, a po wylogowaniu formularz jest teraz unikalny dla każdej strony.
3) Czy należy utworzyć własny szablon motywu, aby to naprawić, czy po prostu dodać metodę getCacheContexts () we własnej wtyczce Block? Lepiej jest utworzyć nowy szablon kompozycji, niż dodawać metodę getCacheContexts (), która zastępuje naturalną kolejność tworzenia kontekstów pamięci podręcznej i może głębiej rozbić metadane w tablicy kompilacji.
źródło
Dla każdego, kto znajdzie to ...
Powodem działania renderowania
content
(lubcontent|without()
) jest to, że w tablicy renderowania znajduje się elementcontent['#cache']
zawierający wszystkie buforowane metadane dla treści.Jeśli nie pozwalają na to, aby być renderowane w gałązki, albo
content
albo{{'#cache': content['#cache']|render }}
, strona nie wie, że ma Cacheable metadane (np. Nigdy nie propaguje w górę).Wygląda na to, że nie robisz niestandardowej gałązki. Jeśli używasz czegoś takiego jak Varnish, może to być również winowajca dla anonimowych użytkowników.
źródło
Natknąłem się również na ten problem, a obejściem tego problemu było utworzenie nowej zmiennej block_content w oparciu o przefiltrowaną wersję głównej zmiennej treści, z wyłączeniem pól niestandardowych, które chcę renderować ręcznie:
Następnie zamiast renderować zmienną „content.body” bezpośrednio później, wywołuję:
Jeśli chcesz renderować każde pole osobno, możesz po prostu dodawać je do filtru „bez”, aby renderowanie zawartości bloku nie powodowało niczego poza poprawnym buforowaniem.
źródło
Najłatwiejszym sposobem osiągnięcia tego jest zadeklarowanie i zdefiniowanie
getCacheContexts()
metodySprawdź dokumentację CacheableDependency , powinna ona zawierać wszystko, czego potrzebujesz;)
źródło