사용자 정의 블록에서 캐시 컨텍스트를 설정하는 올바른 방법은 무엇입니까?


13

페이지 당 고유해야하는 블록이 로그 아웃 한 사용자에게 적합하지 않은 문제가 발생했습니다. 문제는 사용자 정의 필터를 포함하는 뷰 검색 페이지에있는 사용자 정의 블록 플러그인입니다 (노출 된 필터의 사용자 정의 대체물과 같습니다.

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 '] ['컨텍스트 ']

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) 아니요, 블록 컨텐츠는 실제로 레벨이 낮아 나중에 병합해야합니다. 2) 1, 3) 때문에 필요하지 않습니다. getCacheContexts ()를 구현하는 것이 더 쉬울 수 있지만 필요하지는 않습니다. 익명 사용자를 명시 적으로 언급했지만 일반 인증 된 사용자에게도 영향을 미치지 않습니까? dynamic_page_cache를 비활성화하면 문제가 해결됩니까? 내부 페이지 캐시는 항상 url / query args에 따라 항상 다르기 때문에 anon 사용자에게만 영향을 미치는 경우 이상한 일이 발생해야합니다.
Berdir

1
동적 페이지 캐시를 비활성화해도 문제가 해결되지 않습니다.
oknate

1
흠, 최상위 요소에 #cache 이외의 것이 포함되어 있지 않다는 점에서 문제가 될 수 있습니다. 양식 내에 #cache를 설정하려고 했습니까? 그것들은 그것들에 따라 다양해야하며 캐시 태그가 버블 링되기 때문에 작동해야합니다. 그리고 다른 블록이나 다른 장소에서 양식을 사용하는 경우에도 작동해야합니다.
Berdir

1
나는 SyndicateBlock을 빠르게 해킹 하고이 build () 메소드를 사용했습니다 : gist.github.com/Berdir/33a31b1e98caf080dae78adb731dba4c . 내 사이트에서 제대로 작동하면 캐시 컨텍스트가 cache_render 테이블에 표시되고 올바른 요청 URI가 표시됩니다. 당신도 같은 시도 할 수 있습니까? 귀하의 사이트에서 매우 이상한 일이 일어나고있는 것 같습니다
Berdir

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)의 결론은 문제가 있다고 생각한다. 왜냐하면 자신의 캐시 메타 데이터가 버블 링 될 수있을뿐만 아니라 렌더 배열 내부에서 더 깊고 알지 못할 수도 있기 때문입니다.
4k4

새 테마 템플릿을 만드는 것이 좋습니다. 확실한 버블 링을 유지하려면?
oknate

예, 블록 템플릿을 사용하여 외부에 물건을 추가하십시오. build ()에서 블록 내부를 빌드하십시오. 이를 위해 사용자 정의 템플릿을 사용하거나 테이블과 같은 렌더링 요소 또는 링크와 같은 핵심 템플릿을 사용하십시오.
4k4 December

좋아요, 답변을 업데이트하겠습니다.
oknate

어쨌든 나뭇 가지의 시추는 캐시 가능한 메타 데이터를 무시한다는 것을 몰랐습니다. 이는 결국 작은 레벨로 내려 가면서 메타 데이터를 보존하기 위해 드릴 인 (작은 확장을 쓸모 없게 함)하기 위해 자체 사용자 정의 방법을 사용해야 함을 의미 할 수 있습니다. 잘 찾아라!
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 렌더링이 수정 캐싱 이외의 작업을 수행하지 않도록 "without"필터에 계속 추가하면됩니다.


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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.