У меня есть очень простой блок, который просто показывает идентификатор текущего узла.
<?php
/**
* @file
* Contains \Drupal\mymodule\Plugin\Block\ExampleEmptyBlock.
*/
namespace Drupal\mymodule\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;
/**
* @Block(
* id = "example_empty",
* admin_label = @Translation("Example: empty block")
* )
*/
class ExampleEmptyBlock extends BlockBase {
/**
* {@inheritdoc}
*/
public function build() {
$node = \Drupal::routeMatch()->getParameter('node');
$build = array();
if ($node) {
$config = \Drupal::config('system.site');
$build = array(
'#type' => 'markup',
'#markup' => '<p>' . $node->id() . '<p>',
'#cache' => array(
'tags' => $this->getCacheTags(),
'contexts' => $this->getCacheContexts(),
),
);
}
return $build;
}
/**
* {@inheritdoc}
*/
public function getCacheTags() {
$node = \Drupal::routeMatch()->getParameter('node');
return Cache::mergeTags(parent::getCacheTags(), ["node:{$node->id()}"]);
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
return Cache::mergeContexts(parent::getCacheContexts(), ['user.node_grants:view']);
}
}
Но после кэширования блок остается неизменным независимо от того, какой узел я посещаю. Как правильно кешировать результат по идентификатору узла?
getCacheTags()
BlockBase, вам просто нужно добавить тег, который представляет ваш узел (node: {nid}). Извините, я сейчас тороплюсь, позже объясню лучше,Ответы:
Это полный рабочий код с комментариями.
Я проверил это; оно работает.
Просто поместите код в файл с именем NodeCachedBlock.php в папке вашего модуля, измените его пространство имен {имя_модуля}, очистите кеш и используйте его.
источник
#cache
настройки в функции сборки и просто добавить публичные функции?Самым простым способом сделать это является использование системы контекста плагин / блок.
Смотрите мой ответ для Как сделать блок, который извлекает содержимое текущего узла?
Вам просто нужно поместить определение контекста узла в аннотацию вашего блока следующим образом:
И затем используйте это так:
$this->getContextValue('node')
Приятно то, что Drupal позаботится о кешировании для вас. Автоматически. Потому что он знает, что по умолчанию (и насколько ядро идет только) контекст узла является текущим узлом. И он знает, откуда он, поэтому контекст кеша и теги кеша добавляются автоматически.
Через
\Drupal\Core\Plugin\ContextAwarePluginBase::getCacheContexts()
и соответствующиеgetCacheTags()
методы BlockBase / ваш класс блоков расширяется от этого и наследует эти методы.источник
\Drupal::routeMatch()->getParameter('node')
на$this->getContextValue('node')
и решаете всю проблему кеширования одной строкой кода? Большой!Если вы наследуете класс вашего блочного плагина
Drupal\Core\Block\BlockBase
, у вас будет два метода для установки тегов и контекстов кеша.getCacheTags()
getCacheContexts()
Например, блок модуля Book реализует эти методы следующим образом.
Блок модуля Форум использует следующий код.
В вашем случае я бы использовал следующий код.
Вы также можете использовать следующий метод, чтобы сделать блок вообще не кэшируемым (даже если бы я этого избегал). Это может быть полезно в других случаях, возможно.
Не забудьте добавить
use Drupal\Core\Cache\Cache;
в начало файла, если вы собираетесь использоватьCache
класс.источник
BlockBase
класс в качестве родительского класса?При создании массива рендеринга всегда прикрепляйте правильные метаданные:
Это не относится к конкретным блокам, и методы зависимостей кэшей плагинов блоков getCacheTags (), getCacheContext () и getCacheMaxAge () не являются заменой. Их следует использовать только для дополнительных метаданных кэша, которые нельзя доставить через массив визуализации.
Смотрите документацию:
https://www.drupal.org/docs/8/api/render-api/cacheability-of-render-arrays
Посмотрите в этом примере, как Drupal ожидает, что массив рендеринга предоставит необходимые метаданные кэша при оптимизации кэширования с помощью автозаполнения и отложенного построения. Проблема установки пользовательских тегов кэша в пользовательском блоке в контексте пользователя
источник
#markup
можно кэшировать так же, как любой другой элемент рендеринга. В данном случае это не разметка, а блок, который кэшируется, и здесь возникает проблема. Вы не можете решить это с тегами кэша, потому что они становятся недействительными, только если узел изменяется в базе данных.BlockBase
класса есть даже необходимые методы.return [ '#markup' => render($output), '#cache' => [ 'contexts' => ['url'] ] ];
работает очень хорошо для кеширования URL.Проблема здесь в том, что контексты кэша не объявляются в нужном месте в функции сборки:
Если вы вызываете этот блок не на узле, функция сборки возвращает пустой массив, так что для этого блока нет контекста кэширования, и это поведение будет кэшироваться drupal: отображение этого блока не будет корректно аннулировано или визуализировано.
Решение состоит в том, чтобы каждый раз инициализировать $ build с контекстами кэша:
источник
Я понимаю, что опоздал на этот разговор, но приведенный ниже код работал для меня:
источник
Вы пытались реализовать hook_block_view_BASE_BLOCK_ID_alter?
function hook_block_view_BASE_BLOCK_ID_alter (массив & $ build, \ Drupal \ Core \ Block \ BlockPluginInterface $ block) {$ build ['# cache'] ['max-age'] = 0; }
источник