Який правильний спосіб встановити кеш-контексти на спеціальні блоки?


13

У мене виникло питання, коли блок, який повинен бути унікальним на кожній сторінці, не для користувачів, які вийшли з системи. Проблема - це плагін спеціального блоку, який я маю на сторінці пошуку переглядів, яка містить власні фільтри (на зразок власної заміни для відкритих фільтрів. Блок, розміщений через / admin / structure / block).

На основі того, що я дізнався про Drupal 8, я додав контексти кешу до свого масиву збирання:

  public function build() {

    $search_form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\SearchForm');
    return [
      'search_form' => $search_form,
      '#cache' => ['contexts' => ['url.path', 'url.query_args']]
    ];

  }

Але здається, що це повинно бути невірним, оскільки при виході з нього блок буде кешований під час першого перегляду, а коли URL-адреса змінюється, не відображається нова версія блоку.

Я подумав, що причиною проблеми може стати сторінка перегляду, але навіть коли я вимкнув кешування на сторінці перегляду, проблема залишилася.

Мені вдалося виправити проблему кількома способами, наприклад, за допомогою гачка prepcess_block:

function mymodule_preprocess_block__mycustomsearchblock(&$variables) {
  $variables['#cache']['contexts'][] = 'url.path';
  $variables['#cache']['contexts'][] = 'url.query_args';
}

Але це мене непокоїло, що я не міг просто помістити кеш-контексти в масив збірки мого блоку.

Оскільки мій блок поширює BlockBase, я вирішив спробувати метод getCacheContexts (), тим більше, що я бачив, як деякі модулі в ядрі роблять це таким чином.

  public function getCacheContexts() {
    return Cache::mergeContexts(parent::getCacheContexts(), ['url.path', 'url.query_args']);
  }

Це також виправлено проблему, але що цікаво, коли я видаю змінні у функції блоку попередньої обробки, вони не відображаються у $ змінних ['# кеш'] ['контексти'], але вони відображаються в $ змінних ['елементи '] [' # кеш '] [' контексти ']

array:5 [▼
  0 => "languages:language_interface"
  1 => "theme"
  2 => "url.path"
  3 => "url.query_args"
  4 => "user.permissions"
]

Я намагаюся розібратися, як це працює, і чому це не працює від функції збірки.

Дивлячись на /core/modules/block/src/BlockViewBuilder.php на функцію viewMultiple (), схоже, вона витягує теги кешу з сутності та плагіна:

'contexts' => Cache::mergeContexts(
  $entity->getCacheContexts(),
  $plugin->getCacheContexts()
),

Отже, це пояснює, чому додавання методу getCacheContexts () до мого блокового плагіну додає контексти до мого блоку. Крім того, дивлячись на метод preRender у тому ж класі, схоже, що він не використовує масив кешу у функції збирання блоків, що мене бентежить, оскільки, здається, спосіб додавання кешування в Drupal 8 - це додавання #cache елемент для візуалізації елементів.

Отже, моє питання:

1) Чи додаються контексти кешу безпосередньо в масив в блочному плагіні?

2) Якщо так, чи існує спосіб цього, чи потрібно додати його до дочірнього елемента масиву збірки?

3) Якщо контекст, що додається безпосередньо, ігнорується, чи додає getCacheContexts () спосіб перейти на блокові плагіни у користувацьких модулях?


1
1) Ні, вміст вашого блоку насправді знижений і його слід об’єднати пізніше. 2) Не потрібно, оскільки 1, 3) Реалізація getCacheContexts () може бути простішою / чистішою, але її не потрібно. Ви чітко згадуєте анонімних користувачів, ви впевнені, що це також не впливає на звичайних аутентифікованих користувачів? Чи усунеться проблема, якщо вимкнути динамічну сторінку_cache? Щось дивне повинно відбутися, якщо це стосується лише користувачів, які не користуються, оскільки внутрішній кеш сторінки завжди змінюється залежно від аргументів url / query.
Бердір

1
Вимкнення динамічного кешу сторінки не вирішує проблему.
окнат

1
Гм, це може бути проблемою з тим, що елемент верхнього рівня не містить нічого, крім #cache. Ви намагалися просто встановити #cache у вашій формі? Це форма, яка повинна змінюватись в залежності від тих, і оскільки теги кешу збільшуються, це має просто працювати. І якщо ви коли-небудь використовуєте свою форму в іншому блоці чи іншому місці, вона також повинна просто працювати там.
Бердір

1
Я швидко зламав SyndicateBlock і використав цей метод build (): gist.github.com/Berdir/33a31b1e98caf080dae78adb731dba4c . Розмістивши те, що на моєму сайті працює чудово, контексти кешу відображаються в таблиці cache_render і відображається правильний URI запиту. Ви можете спробувати те саме? Мені здається, що на вашому сайті відбувається щось дуже дивне
Бердір

2
Використовуєте ти стандартний блок-шаблон або власний? Дивіться drupal.stackexchange.com/questions/217884/…
4k4

Відповіді:


9

У більшості випадків ви просто встановлюєте контекст кешу безпосередньо на масиві візуалізації, який ви повернете у своєму методі build ().

Нарешті я знайшов, у чому полягає моя проблема, за допомогою @Berdir та @ 4k4. Якщо ви користуєтеся спеціальним шаблоном, таким як block - myblock.html.twig, і ви виводите змінні окремо, наприклад {{content.foo}} замість усіх одночасно, як {{content}}, він ігнорує ваші контексти кешу передавались безпосередньо у ваш масив складання блоку, коли виходили з системи. Див. Який правильний спосіб встановити кеш-контексти на спеціальні блоки?

Отже, щоб відповісти на початкове запитання:

1) Контексти кешу, що передаються безпосередньо у плагін користувальницького блоку, іноді ігноруються. Ви можете перевірити це, змінивши SyndicateBlock , а потім створивши користувальницький шаблон у своєму блоці теми - union.html.php, у якому виводите змінні окремо так:

{% block content %}
  {{ content.foo }}
{% endblock %}

Змінюючи аргументи URL-адреси, ви побачите, що блок не відповідає контексту кешу.

Тепер, якщо ви зміните, виведіть весь вміст як фрагмент, він працює:

{% block content %}
  {{ content }}
{% endblock %}

Тепер він поважає контекст кешу, і блок є унікальним на кожній сторінці.

2) Поки що, щоб обійти це, ви можете просто вивести те, що є у вашому блоці, у власному шаблоні.

 public function build() {

    $search_form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\SearchForm');
    return [
      '#theme' => 'mycustomtemplate',
      '#search_form' => $search_form,
      '#cache' => ['contexts' => ['url.path', 'url.query_args']]
    ];

  }

Це уникає винятків езотеричного кешування блоку модуля, і ваша форма тепер унікальна на кожній сторінці при виході з системи.

3) Чи слід створити власний шаблон теми, щоб виправити це, або просто додати метод для getCacheContexts () у свій користувацький плагін Block? Краще створити новий шаблон теми, а не додавати метод getCacheContexts (), який переосмислює природний порядок переповнення контекстів кешу і може порушити метадані глибше у масиві збірки.


Це дуже хороший підсумок проблеми. Але я вважаю, що висновок у пункті 3) є проблематичним, тому що ви не тільки порушуєте, що ваші власні метадані кеша можуть міхурнути, але і ті, які можуть знаходитися глибше всередині масиву візуалізації, і ви, можливо, не знаєте про це.
4k4

Тож ви б запропонували створити новий шаблон теми? Щоб зберегти чіткий барботаж?
окнат

Так, використовуйте шаблон блоку лише для додання речей зовні. Побудуйте внутрішню частину блоку в build (). Використовуйте для цього спеціальні шаблони або використовуйте елементи візуалізації, як-от таблицю або основний шаблон, як-от посилання.
4k4

Гаразд, я оновлю відповідь.
окнат

Фу, я не розумів, що свердління Twig ігнорує кеширувані метадані. Це може означати, що нам потрібно використовувати власний користувальницький метод врешті-решт, щоб висвердлити (що робить розширення гілочки марним), щоб ми зберегли метадані під час опускання на нижчі рівні. Гарна знахідка!
LionsAd

4

Для всіх, хто знайде це ...

Причиною того, що візуалізація content(або content|without()) працює, є те, що в масиві візуалізації є елемент, content['#cache']який містить усі кешовані метадані для вмісту.

Якщо ви не дозволите, щоб це відображалося в гілочці, contentабо {{'#cache': content['#cache']|render }}сторінка не знає, що в ньому є кешовані метадані (наприклад, вона ніколи не розмивається).

Схоже, ви не робите спеціальних гілочок. Якщо ви використовуєте щось на зразок лаку, це також може бути винуватцем для анонімних користувачів.


3

Я також зіткнувся з цією проблемою, і вирішив мені створити нову змінну block_content на основі відфільтрованої версії основної змінної вмісту, виключаючи будь-які спеціальні поля, які я хочу візуалізувати вручну:

{% set block_content = content|without('field_mycustomfield', 'field_mycustomfield2') %}

Тоді замість того, щоб передати змінну "content.body" безпосередньо пізніше, я закликаю:

{{ block_content }}

Якщо ви хочете візуалізувати кожне поле окремо, ви можете просто додавати їх у фільтр "без", щоб рендеринг block_content не робив нічого, крім кешування виправлення.


0

Найпростіший спосіб досягти цього - оголосивши та визначивши getCacheContexts()метод


  public function build() {

    $search_form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\SearchForm');
    return [
      'search_form' => $search_form
    ];

  }

  /**
   * {@inheritdoc}
   */
  public function getCacheMaxAge() {
    // If you need to redefine the Max Age for that block
    return 0;
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheContexts() {
    return ['url.path', 'url.query_args'];
  }

Перегляньте документацію CacheableDependency , вона повинна містити все необхідне;)


Це більше не працює так, як зараз відображаються блоки, дивіться drupal.stackexchange.com/questions/288881/…
4k4
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.