web-dev-qa-db-ja.com

特定のタイプのノードが保存または更新されたときにキャッシュされたブロックを更新する正しい方法は何ですか?

ブロックプラグインを含むカスタムモジュールがあります。

SliderBlock.php

class SliderBlock extends BlockBase {
  public function build() {
    $build = [];
    $build['#theme'] = 'slider_block';

    return $build;
  }
}

ブロック内に「スライド」タイプのノードのリストをレンダリングしたい(最終的にはカスタムスライダー機能になります)

mymodule.module

function mymodule_theme() {
  return [
    'slides_block' => [
      'variables' => [
        'slides' => get_slides(),
      ],
    ],
  ];
}

function get_slides() {
  // Load all slides nodes.
  $nids = \Drupal::entityQuery('node')
    ->condition('type','slides')
    ->condition('status','1') // Only published slides nodes.
    ->execute();
  $nodes =  \Drupal\node\Entity\Node::loadMultiple($nids);

  return $nodes;
}

そして、テンプレートのそれぞれをループして、必要なマークアップを取得します。

slider-block.html.twig

{# markup for slider here #}
{% for slide in slides %}
  <p>{{ slide.label }}</p>
{% endfor %}

上記のテンプレートは、各スライドノードのタイトルフィールドを示しています。問題は、スライドノードを非公開にした場合、キャッシュをクリアしない限りマークアップが更新されないことです。

これはキャッシュの問題のように聞こえるので、Googleの検索ミッションを調べて、ブロックのキャッシュを無効にする一連の方法を発見しました。匿名ユーザーやその他の場合、どれも機能しないようです:

// Unsuccessful:
public function getCacheMaxAge() {
  return 0;
}

// Unsuccessful:
$build['#cache'] = [
  'max-age' => 0,
];

私が知る限り、キャッシュコンテキストを調べましたが、「ノード」ベースのキャッシュコンテキストはありませんでした。 「スライド」タイプの特定のノードのIDがわからないため、それらを正しく理解していれば、キャッシュタグは役に立ちません。

このブロックのキャッシュを無効にしたいだけです。このブロックは特定のページでのみレンダリングされるので、モジュールのルートymlファイルを使用してそのページのキャッシュを無効にすることができます(私は正しいですか)。ただし、1つのブロックのためにページ全体を無効にするのは間違っています。

これを解決する正しい方法は何ですか?そしてその結論に到達するための方法論は何ですか?

:ビューを使用してノードを収集できますが、ビューのマークアップをカスタマイズするのはより困難です。

4
RominRonin

@Clive コメント のように、ノードリストキャッシュタグが必要です。

(hook_theme()ではなく)ビルド配列でテンプレートを呼び出すときに、スライドとキャッシュタグを配置します。このフックは、モジュールをインストールするときにのみ呼び出されます。

class SliderBlock extends BlockBase {
  public function build() {
    $build = [
     '#theme' => 'slider_block',
     '#slides' => getSlides(),
     '#cache' => ['tags' => ['node_list']],
    ];
    return $build;
  }
}

Drupal 8.9.xに更新したら、コードにTODOアノテーションを入れて、リストタグをコンテンツタイプ固有のnode_list:slidesに変更します。この変更レコードを参照してください。 ENTITY_TYPE_list:BUNDLEキャッシュタグを追加しました。

3
4k4

@Cliveと@leymannxのコメントで述べたように、キャッシュタグを使用してジョブを実行する必要があります。問題は、ノードがテーマフックによって読み込まれているため、現在のアプローチが難しいことです。コードをリファクタリングし、ノードをブロックでロードするようにすれば、Drupalの方法がよりクリーンになり、テーマフックがデータの準備とレンダリングに集中できるようになります。あなたのコードは次のようになる可能性があります(依存性注入は意図的に省略されています、これについて詳しく読んでください ここ ):

SliderBlock.php

class SliderBlock extends BlockBase {
  private $slides;

  public function build() {
    $this->slides = $this->getSlides();

    if (empty($this->slides)) {
      // Feel free to change to your use case when
      // no slides available.
      // Keep in mind that any non-empty render array
      // could/would be cached.
      return [];
    }

    $build = [];
    $build['#theme'] = 'slider_block';
    $build['#slides'] = $this->slides;

    return $build;
  }

  function getSlides() {
    // Load all slides nodes.
    $nids = \Drupal::entityQuery('node')
      ->condition('type','slides')
      ->condition('status','1') // Only published slides nodes.
      ->execute();
    return \Drupal\node\Entity\Node::loadMultiple($nids);
  }

  public function getCacheTags() {
    $cache_tags = parent::getCacheTags();
    $cache_tags[] = 'block:' . $this->getDerivativeId();

    if (empty($this->slides)) {
       return $cache_tags;
    }

    $node_cache_tags = [];

    foreach ($this->slides as $node) {
      $node_cache_tags[] = $node->getCacheTags();
    }

    $node_cache_tags = array_merge([], ...$node_cache_tags);
    $cache_tags = \Drupal\Core\Cache\Cache::mergeTags($cache_tags, $node_cache_tags);

    return $cache_tags;
  }
}

...a.k.aスプレッド演算子と、なぜ私がそのようなタグをマージしたのかについて懸念がある場合は、 この投稿 を確認してください。

mymodule.module

function mymodule_theme() {
  return [
    'slides_block' => [
      'variables' => [
        'slides' => NULL,
      ],
    ],
  ];
}

また、ブロックキャッシュタグについて この投稿 も確認してください。

2
d70rr3s