web-dev-qa-db-ja.com

カスタムブロックにキャッシュコンテキストを設定する正しい方法は何ですか?

ページごとに固有でなければならないブロックがログアウトしたユーザーのものではないという問題に遭遇しました。問題は、カスタムフィルターを含むビュー検索ページにあるカスタムブロックプラグインです(公開されたフィルターのカスタム置換のようなものです。ブロックは/ 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が変更されてもブロックの新しいバージョンが表示されなかったため、これは正しくないようです。

問題の原因となっているのはビューページの可能性があると思いましたが、ビューページのキャッシュをオフにしても問題は解決しませんでした。

たとえば、preprocess_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']);
  }

これも問題が修正されましたが、興味深いことに、プリプロセスブロック関数で変数を出力すると、変数は$ variables ['#cache'] ['contexts']に表示されませんが、$ variables ['elementsには表示されます'] ['#cache '] [' contexts ']

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

これがどのように機能するのか、そしてなぜそれがビルド機能から機能しなかったのかを理解しようとしています。

ViewMultiple()関数で/core/modules/block/src/BlockViewBuilder.phpを見ると、エンティティとプラグインからキャッシュタグを取得しているように見えます。

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

つまり、ブロックプラグインにgetCacheContexts()メソッドを追加すると、コンテキストがブロックに追加される理由がわかります。また、同じクラスのpreRenderメソッドを見ると、ブロック構築関数でキャッシュ配列を使用していないように見えます。これは、Drupal 8でキャッシュを追加する方法が#cache要素を追加して要素をレンダリングします。

だから私の質問は、

1)ブロックプラグインの配列に直接追加されたキャッシュコンテキストは無視されますか?

2)もしそうなら、それを回避する方法はありますか、それをビルド配列の子要素に追加する必要がありますか?

3)直接追加されたコンテキストが無視される場合、カスタムモジュールでブロックプラグインを取得する方法としてgetCacheContexts()を追加していますか?

13
oknate

ほとんどの場合、build()メソッドで返すレンダー配列にキャッシュコンテキストを直接設定するだけです。

@Berdirと@ 4k4の助けを借りて、ようやく私の問題が見つかりました。 block--myblock.html.twigなどのカスタムテンプレートを使用していて、{{content}}のように同時にすべてではなく、{{content.foo}}などの変数を個別に出力する場合は、無視されますログアウトすると、キャッシュコンテキストがブロックビルド配列に直接渡されます。参照 カスタムブロックにキャッシュコンテキストを設定する正しい方法は何ですか?

だから、元の質問に答えるには:

1)カスタムブロックプラグインに直接渡されたキャッシュコンテキストが無視されることがあります。これを SyndicateBlockを変更する でテストしてから、テーマブロック--syndicate.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()のメソッドを追加するだけですか?キャッシュコンテキストのバブリングの自然な順序をオーバーライドするgetCacheContexts()メソッドを追加するよりも、新しいテーマテンプレートを作成することをお勧めします。

9
oknate

これを見つけた他の人のために...

content(またはcontent|without())のレンダリングが機能する理由は、コンテンツのキャッシュ可能なすべてのメタデータを含む要素がレンダリング配列content['#cache']にあるためです。

これをcontentまたは{{'#cache': content['#cache']|render }}のいずれかでtwigにレンダリングすることを許可しない場合、ページキャッシュ可能なメタデータがあることを知らない(例:決して泡立つことはありません) 。

ただし、カスタムtwigを使用していないようです。ワニスのようなものを使用している場合は、匿名ユーザーの犯人になる可能性もあります。

4
twill

私もこの問題に遭遇し、私が思いついた回避策は、手動でレンダリングしたいカスタムフィールドを除いて、メインコンテンツ変数のフィルターバージョンに基づいて新しいblock_content変数を作成することでした:

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

次に、「content.body」変数を後で直接レンダリングする代わりに、以下を呼び出します。

{{ block_content }}

すべてのフィールドを個別にレンダリングしたい場合は、「なし」フィルターにフィールドを追加し続けることで、block_contentのレンダリングが修正キャッシュ以外に何もしないようにすることができます。

3
Ryan Barkley

これを実現する簡単な方法は、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 のドキュメントを確認してください。必要なものがすべて含まれているはずです;)

0
Ben Cassinat