Qual è il modo corretto di impostare contesti cache su blocchi personalizzati?


13

Ho riscontrato un problema in cui un blocco che dovrebbe essere univoco per pagina non è destinato agli utenti disconnessi. Il problema è un plug-in di blocco personalizzato che ho su una pagina di ricerca delle viste che contiene filtri personalizzati (una specie di rimpiazzo personalizzato per i filtri esposti. Il blocco inserito attraverso / admin / struttura / blocco).

Sulla base di ciò che ho imparato su Drupal 8, ho aggiunto i contesti di cache al mio array di build:

  public function build() {

    $search_form = \Drupal::formBuilder()->getForm('Drupal\mymodule\Form\SearchForm');
    return [
      'search_form' => $search_form,
      '#cache' => ['contexts' => ['url.path', 'url.query_args']]
    ];

  }

Ma sembra che questo non sia corretto perché quando disconnesso, il blocco verrebbe memorizzato nella prima vista e, quando l'URL cambiava, non mostrava una nuova versione del blocco.

Ho pensato che potesse essere la pagina di visualizzazione a causare il problema, ma anche quando ho disattivato la memorizzazione nella cache nella pagina di visualizzazione, il problema è rimasto.

Sono stato in grado di risolvere il problema in diversi modi, ad esempio utilizzando un hook preprocess_block:

function mymodule_preprocess_block__mycustomsearchblock(&$variables) {
  $variables['#cache']['contexts'][] = 'url.path';
  $variables['#cache']['contexts'][] = 'url.query_args';
}

Ma mi dava fastidio non poter semplicemente inserire i contesti della cache nell'array di build del mio blocco.

Poiché il mio blocco estende BlockBase, ho deciso di provare il metodo getCacheContexts (), soprattutto perché ho visto che alcuni moduli all'interno di core lo stanno facendo in questo modo.

  public function getCacheContexts() {
    return Cache::mergeContexts(parent::getCacheContexts(), ['url.path', 'url.query_args']);
  }

Questo risolve anche il problema, ma è interessante notare che quando eseguo l'output delle variabili nella funzione del blocco di preelaborazione, queste non vengono visualizzate in $ variabili ['# cache'] ['contesti'], ma mostrano in $ variabili ['elementi '] [' # di cache '] [' contesti]

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

Sto cercando di capire come funziona e perché non funziona dalla funzione build.

Guardando /core/modules/block/src/BlockViewBuilder.php nella funzione viewMultiple (), sembra che tira i tag cache dall'entità e dal plugin:

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

Questo spiega perché l'aggiunta di un metodo getCacheContexts () al mio plug-in di blocco aggiunge i contesti al mio blocco. Inoltre, guardando il metodo preRender nella stessa classe, sembra che non usi l'array cache nella funzione build di blocchi, il che mi confonde, poiché sembra che il modo per aggiungere la cache in Drupal 8 sia aggiungere un #cache elemento per il rendering di elementi.

Quindi la mia domanda è

1) I contesti cache aggiunti direttamente sull'array in un plug-in di blocco vengono ignorati?

2) In tal caso, c'è un modo per aggirare questo, dobbiamo aggiungerlo a un elemento figlio dell'array build?

3) Se il contesto aggiunto direttamente viene ignorato, l'aggiunta di getCacheContexts () è la strada da percorrere per i plug-in di blocco nei moduli personalizzati?


1
1) No, il contenuto del tuo blocco è in realtà di un livello inferiore e dovrebbe essere unito in un secondo momento. 2) Non necessario perché 1, 3) L'implementazione di getCacheContexts () può essere più semplice / più pulita, ma non dovrebbe essere richiesta. Citi esplicitamente gli utenti anonimi, sei sicuro che non influisca anche sui normali utenti autenticati? Il problema scompare se si disabilita dynamic_page_cache? Deve succedere qualcosa di strano se interessa solo un utente, poiché la cache della pagina interna varia sempre in base agli argomenti url / query.
Berdir,

1
La disabilitazione della cache dinamica della pagina non risolve il problema.
Oknate,

1
Hm, potrebbe essere un problema con il fatto che il tuo elemento di livello superiore non contiene nulla tranne #cache. Hai provato a impostare semplicemente #cache nel tuo modulo? È il modulo che deve variare da quelli, e poiché i tag della cache si gonfiano, dovrebbe funzionare. E se mai usi il tuo modulo in un blocco diverso o in un altro posto, dovrebbe funzionare anche lì.
Berdir,

1
Ho subito hackerato SyndicateBlock e usato questo metodo build (): gist.github.com/Berdir/33a31b1e98caf080dae78adb731dba4c . Ponendo che sul mio sito funzioni bene, i contesti della cache sono visibili nella tabella cache_render e viene visualizzato l'URI di richiesta corretto. Puoi provare lo stesso? Mi sembra che sul tuo sito stia succedendo qualcosa di molto strano
Berdir,

2
Usi il modello a blocchi standard o uno personalizzato? Vedi drupal.stackexchange.com/questions/217884/…
4k4

Risposte:


9

Nella maggior parte dei casi, è sufficiente impostare il contesto della cache direttamente sull'array di rendering restituito nel metodo build ().

Ho finalmente trovato il mio problema, con l'aiuto di @Berdir e @ 4k4. Se si utilizza un modello personalizzato, come block - myblock.html.twig e si producono le variabili singolarmente, come {{content.foo}} invece di tutti contemporaneamente come {{content}}, ignora i contesti della cache passati direttamente nell'array di generazione blocchi, quando si è disconnessi. Vedi Qual è il modo corretto di impostare contesti cache su blocchi personalizzati?

Quindi, per rispondere alla domanda originale:

1) I contesti della cache passati direttamente in un plug-in di blocco personalizzato vengono talvolta ignorati. Puoi testarlo modificando SyndicateBlock e quindi creando un modello personalizzato nel tuo blocco a tema - syndicate.html.php in cui produci le variabili singolarmente in questo modo:

{% block content %}
  {{ content.foo }}
{% endblock %}

Man mano che modifichi gli argomenti url, vedrai che il blocco non rispetta il contesto della cache.

Ora se lo cambi emetti tutto il contenuto in un unico pezzo, funziona:

{% block content %}
  {{ content }}
{% endblock %}

Ora rispetta il contesto della cache e il blocco è unico per pagina.

2) Per ora, per ovviare a questo, puoi semplicemente generare ciò che è nel tuo blocco nel suo modello.

 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']]
    ];

  }

Ciò elude le eccezioni di memorizzazione nella cache esoterica del modulo a blocchi e il tuo modulo è ora unico per pagina quando sei disconnesso.

3) Dovresti creare il tuo modello di tema per risolvere questo problema o semplicemente aggiungere un metodo per getCacheContexts () nel tuo plug-in Block personalizzato? È meglio creare un nuovo modello di tema, piuttosto che aggiungere un metodo getCacheContexts () che ignora l'ordine naturale di gorgogliamento dei contesti della cache e potrebbe interrompere i metadati più in profondità nell'array di build.


Questo è un ottimo riassunto del problema. Ma penso che la conclusione in 3) sia problematica, perché non solo rompi il fatto che i tuoi metadati della cache possano essere fatti capovolgere, ma anche quello che potrebbe essere più profondo all'interno dell'array di rendering e potresti non esserne consapevole.
4k4,

Quindi suggeriresti di creare un nuovo modello di tema? Per preservare il gorgoglio chiaro?
Oknate,

Sì, usa il modello a blocchi solo per aggiungere elementi all'esterno. Costruisci l'interno del blocco in build (). Utilizzare modelli personalizzati per questo o utilizzare elementi di rendering, come una tabella o un modello principale come i collegamenti.
4k4,

OK, aggiornerò la risposta.
Oknate,

Ugh, non mi ero reso conto che la penetrazione di Twig avrebbe ignorato i metadati memorizzabili nella cache. Ciò potrebbe significare che alla fine dovremo utilizzare il nostro metodo personalizzato per eseguire il drill in (che rende inutile l'estensione del ramoscello) in modo da preservare i metadati scendendo ai livelli inferiori. Buona scoperta!
LionsAd

4

Per chiunque lo trovi ...

Il motivo per cui il rendering content(o content|without()) funziona è che nella matrice di rendering è presente un elemento content['#cache']che contiene tutti i metadati memorizzati nella cache per il contenuto.

Se non si consente il rendering di questo in un ramoscello, o contento {{'#cache': content['#cache']|render }}, la pagina non sa di avere metadati memorizzabili nella cache (ad es. Non si riempie mai di bolle).

Sembra che tu non stia facendo un ramoscello personalizzato. Se stai usando qualcosa come Varnish, questo potrebbe anche essere un colpevole per gli utenti anonimi.


3

Ho anche riscontrato questo problema e la soluzione alternativa che ho trovato è stata quella di creare una nuova variabile block_content basata su una versione filtrata della variabile di contenuto principale escludendo tutti i campi personalizzati che voglio rendere manualmente:

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

Quindi, invece di eseguire il rendering della variabile "content.body" direttamente più tardi, chiamo:

{{ block_content }}

Se si desidera eseguire il rendering di ogni campo singolarmente, è possibile continuare ad aggiungerli al filtro "senza" in modo che il rendering block_content non faccia altro che correggere la cache.


0

Il metodo più semplice per raggiungere questo obiettivo è dichiarare e definire il getCacheContexts()metodo


  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'];
  }

Controlla la documentazione di CacheableDependency , dovrebbe contenere tutto il necessario;)


Questo non funziona più nel modo in cui vengono ora visualizzati i blocchi, vedi drupal.stackexchange.com/questions/288881/…
4k4
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.