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


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が変更されてもブロックの新しいバージョンが表示されなかったため、これは正しくないようです。

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

たとえば、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()を追加していますか?


1
1)いいえ、ブロックのコンテンツは実際には1レベル下にあり、後でマージする必要があります。2)1、3)getCacheContexts()の実装はより簡単/よりクリーンになる可能性があるため不要ですが、必須ではありません。匿名ユーザーについて明示的に言及していますが、それが通常の認証済みユーザーにも影響を及ぼさないことを確信していますか?dynamic_page_cacheを無効にすると、問題はなくなりますか?いずれにしても内部ページキャッシュは常にurl / query argsによって変化するため、それがanonユーザーにのみ影響を与える場合、何か奇妙なことが発生するはずです。
Berdir

1
動的ページキャッシュを無効にしても問題は解決しません。
oknate 2016

1
うーん、最上位の要素に#cache以外のものが含まれていないという事実に問題があるかもしれません。フォーム内で単に#cacheを設定しようとしましたか?それはそれらによって変化する必要があるフォームであり、キャッシュタグが浮上するので、それはうまくいくはずです。また、別のブロックや他の場所でフォームを使用する場合でも、フォームはそこで機能するはずです。
Berdir

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}}のように同時にすべてではなく、{{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()メソッドを追加するよりも、新しいテーマテンプレートを作成することをお勧めします。


これは問題の非常に良い要約です。しかし、私は3)の結論には問題があると思います。自分のキャッシュメタデータがバブルアップする可能性があるだけでなく、レンダーアレイの内部がより深く、気付かない可能性があるためです。
2016

新しいテーマテンプレートを作成することをお勧めしますか?明確な泡立ちを維持するには?
oknate 2016

はい、ブロックテンプレートは外部に追加する目的でのみ使用してください。build()でブロックの内側をビルドします。これにはカスタムテンプレートを使用するか、テーブルなどのレンダリング要素やリンクなどのコアテンプレートを使用します。
2016

はい、答えを更新します。
oknate 2016

ああ、Twigがドリルインしてもキャッシュ可能なメタデータが無視されるとは思いもしませんでした。これは、最終的に独自のカスタムメソッドを使用してドリルダウンする必要があることを意味し(twig拡張機能が役に立たなくなるため)、下位レベルに移動するときにメタデータを保持します。良い発見!
LionsAd 2018

4

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

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

contentまたはでこれをtwigでレンダリングすることを許可しない場合{{'#cache': content['#cache']|render }}、ページキャッシュ可能なメタデータがあることを認識しません(たとえば、決してバブルアップしません)。

あなたはカスタム小枝をやっていないようですが。Varnishのようなものを使用している場合、それも匿名ユーザーの犯人になる可能性があります。


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 /
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.