Geçerli düğüme bağlı olarak içerik gösteren özel bloğum için önbelleği nasıl doğru şekilde ayarlayabilirim?


19

Sadece geçerli düğümün kimliğini gösteren bu çok temel blok var.

<?php

/**
 * @file
 * Contains \Drupal\mymodule\Plugin\Block\ExampleEmptyBlock.
 */

namespace Drupal\mymodule\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;

/**
 * @Block(
 *   id = "example_empty",
 *   admin_label = @Translation("Example: empty block")
 * )
 */
class ExampleEmptyBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
    $node = \Drupal::routeMatch()->getParameter('node');
    $build = array();

    if ($node) {
      $config = \Drupal::config('system.site');

      $build = array(
        '#type' => 'markup',
        '#markup' => '<p>' . $node->id() . '<p>',
        '#cache' => array(
          'tags' => $this->getCacheTags(),
          'contexts' => $this->getCacheContexts(),
        ),
      );
    }

    return $build;
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheTags() {
    $node = \Drupal::routeMatch()->getParameter('node');
    return Cache::mergeTags(parent::getCacheTags(), ["node:{$node->id()}"]);
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheContexts() {
    return Cache::mergeContexts(parent::getCacheContexts(), ['user.node_grants:view']);
  }

}

Ancak önbelleğe alındıktan sonra, hangi düğümü ziyaret ettiğime bakılmaksızın blok aynı kalır. Düğüm kimliği başına sonucu nasıl doğru şekilde önbelleğe alırım?


1
Bak getCacheTags()(: {nid} düğüm) BlockBase dan, sadece düğümü temsil etiket eklemek gerekmez. Üzgünüm, şimdi acelem var, daha sonra açıklayabilirim,
Vagner

Yanıtlar:


31

Bu yorumlarla dolu çalışma kodu.

namespace Drupal\module_name\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;

/**
 * Provides a Node cached block that display node's ID.
 *
 * @Block(
 *   id = "node_cached_block",
 *   admin_label = @Translation("Node Cached")
 * )
 */
class NodeCachedBlock extends BlockBase {
  public function build() {
    $build = array();
    //if node is found from routeMatch create a markup with node ID's.
    if ($node = \Drupal::routeMatch()->getParameter('node')) {
      $build['node_id'] = array(
        '#markup' => '<p>' . $node->id() . '<p>',
      );
    }
    return $build;
  }

  public function getCacheTags() {
    //With this when your node change your block will rebuild
    if ($node = \Drupal::routeMatch()->getParameter('node')) {
      //if there is node add its cachetag
      return Cache::mergeTags(parent::getCacheTags(), array('node:' . $node->id()));
    } else {
      //Return default tags instead.
      return parent::getCacheTags();
    }
  }

  public function getCacheContexts() {
    //if you depends on \Drupal::routeMatch()
    //you must set context of this block with 'route' context tag.
    //Every new route this block will rebuild
    return Cache::mergeContexts(parent::getCacheContexts(), array('route'));
  }
}

Test ettim; işe yarıyor.

Kodu modül klasörünüzdeki NodeCachedBlock.php adlı bir dosyaya koyun, {module_name} ad alanını değiştirin, önbelleği temizleyin ve kullanın.


böylece hile #cacheyapı işlevindeki ayarları kaldırmak ve sadece ortak işlevleri eklemek için?
Alex

3
Önbellek etiketlerini ve bağlamlarını nerede ayarladığınız önemli değildir.
4k4

Bence bu daha mantıklı, çünkü bir blok inşa ediyoruz, bu yüzden bloğun önbelleğe alınması gerekiyor. İleride bloğunuzu değiştirirseniz (örneğin, fazladan oluşturma öğeleri koyarsanız), bloğunuz çalışır.
Vagner

@ 4k4 url.path da çalışmış gibiydi. fark ne?
Alex

2
@Vagner: Oluşturma dizisine önbellek etiketlerini / bağlamlarını koymak da kötü bir fikir değildir, çünkü verilerinizin bağlı olduğu yere sahip olursunuz. Ve her zaman kabarır, bu yüzden yukarıdaki öğeler hakkında endişelenmenize gerek yoktur. Btw. kodunuz harika, önbellek sorunlarını çok iyi açıklıyor.
4k4

13

Bunu yapmanın en kolay yolu, eklenti / blok bağlam sistemine güvenmektir.

Benim cevap bakınız ben şimdiki düğüm içeriği çeker bir blok nasıl yaparım?

Blok ek açıklamalarınıza şöyle bir düğüm bağlam tanımı koymanız yeterlidir:

*   context = {
*     "node" = @ContextDefinition("entity:node", label = @Translation("Node"))
*   }

Ve sonra böyle kullanın: $this->getContextValue('node')

Bununla ilgili güzel olan şey Drupal'ın sizin için önbelleğe almasıdır. Otomatik olarak. Çünkü varsayılan (ve yalnızca çekirdek olduğu kadarıyla) düğüm bağlamının geçerli düğüm olduğunu bilir. Bu nereden geldiğini bilir, böylece önbellek içeriği ve önbellek etiketleri otomatik olarak eklenir.

Karşılık \Drupal\Core\Plugin\ContextAwarePluginBase::getCacheContexts()gelen getCacheTags()yöntemler ve BlockBase / blok sınıfınız bundan genişler ve bu yöntemleri devralır.


Sen yerini \Drupal::routeMatch()->getParameter('node')ile $this->getContextValue('node')ve bir satır kod ile tüm önbelleğe alma sorunu çözmek? Harika!
4k4

1
şimdiye kadar teşekkürler! tam kod örneği verebilir misiniz?
Alex

@Alex: Sorunuzu düzenledim. Herhangi bir hata bulursanız lütfen kodu kontrol edin ve değiştirin.
4k4

@ 4k4 Ben denemedim çünkü diğer çözüm de çalışıyor
Alex


7

Blok eklentinizin sınıfını türetirseniz, Drupal\Core\Block\BlockBaseönbellek etiketlerini ve bağlamlarını ayarlamak için iki yönteminiz olacaktır.

  • getCacheTags()
  • getCacheContexts()

Örneğin, Kitap modülü bloğu bu yöntemleri aşağıdaki gibi uygular.

  /**
   * {@inheritdoc}
   */
  public function getCacheContexts() {
    return Cache::mergeContexts(parent::getCacheContexts(), ['route.book_navigation']);
  }

Forum modül bloğu aşağıdaki kodu kullanır.

  /**
   * {@inheritdoc}
   */
  public function getCacheContexts() {
    return Cache::mergeContexts(parent::getCacheContexts(), ['user.node_grants:view']);
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheTags() {
    return Cache::mergeTags(parent::getCacheTags(), ['node_list']);
  }

Senin durumunda, ben aşağıdaki kodu kullanırdım.

  /**
   * {@inheritdoc}
   */
  public function getCacheTags() {
    $node = \Drupal::routeMatch()->getParameter('node');
    return Cache::mergeTags(parent::getCacheTags(), ["node:{$node->id()}"]);
  }

Ayrıca, bloğu hiç önbelleğe alınamaz hale getirmek için aşağıdaki yöntemi kullanabilirsiniz (bundan kaçınsam bile). Belki başka durumlarda da faydalı olabilir.

  /**
   * {@inheritdoc}
   */
  public function getCacheMaxAge() {
    return 0;
  }

Sınıfı use Drupal\Core\Cache\Cache;kullanacaksanız, dosyanın üstüne eklemeyi unutmayın Cache.


teşekkürler, ama önbellek temizlendikten sonra / düğüm / 1 ilk başta düğüm / 1 ziyaret ettiğinde blok hala 1 çıktısı
Alex

2
Etkinleştirilmiş bir modülü düzenliyorsanız, düzenlemeden önce modülü kaldırmanız gerekir. Önbelleği temizlemek yeterli değildir.
kiamlaluno

tamam, ama maxAge 0 ekleyerek garip!
Alex

Ayrıca, blok sınıfınız BlockBasesınıfı üst sınıf olarak kullanıyor mu?
kiamlaluno

evet bunu kullanıyor
Alex

3

Bir oluşturma dizisi oluşturduğunuzda, her zaman doğru meta verileri ekleyin:

use Drupal\Core\Cache\Cache;

$build['node_id'] = [
  '#markup' => '<p>' . $node->id() . '<p>',
  '#cache' => [
    'tags' => $node->getCacheTags(),
    // add a context if the node is dependent on the route match
    'contexts' => ['route'],
  ],
];

Bu, bloğa özgü değildir ve blok eklentilerinin önbellek bağımlılık yöntemleri getCacheTags (), getCacheContext () ve getCacheMaxAge () yerine geçmez. Yalnızca oluşturma dizisi üzerinden iletilemeyen ek önbellek meta verileri için kullanılmalıdır.

Belgelere bakın:

"Render API'sını bir render dizisinin önbelleğe alınabilirliği konusunda bilgilendirmeniz son derece önemlidir."

https://www.drupal.org/docs/8/api/render-api/cacheability-of-render-arrays

Drupal otomatik placeholdering ve tembel-bina içinden önbelleğe optimize ederken gerekli bir önbellek meta verilerini sağlayan dizi işlemek beklediğini nasıl bu örneğe bakın kullanıcı bağlamı ile özel blokta Sorun ayarı kullanıcı belirli önbellek etiketleri


Bunun Block nesnesinin önbelleğini ayarlayabileceğini sanmıyorum. '#markup' yalnızca bir Render Öğesi nesnesidir ve önbellek içeriğini veya etiketini ayarlamak için bir neden yoktur. Önbellek geçersiz kılındığında yeniden oluşturulması gereken blok nesnesi.
Vagner

#markupdiğer oluşturma öğeleriyle aynı şekilde önbelleğe alınabilir. Bu durumda işaretleme değil, önbelleğe alınan blok ve sorun burada. Önbellek etiketleriyle çözemezsiniz, çünkü yalnızca veritabanında düğüm değiştirilirse geçersiz hale gelirler.
4k4

@Vagner Bir Block nesnesinin önbelleğini ayarlayabilirsiniz; BlockBaseSınıf gerekli yöntemleri bile vardır.
kiamlaluno

1
Benim return [ '#markup' => render($output), '#cache' => [ 'contexts' => ['url'] ] ];için URL önbellekleme için süper iyi çalışıyor.
leymannx

1
Evet, @leymannx, bu kadar basit. Bu konu, konuyu düşünüyor gibi görünüyor.
4k4

0

Buradaki sorun, önbellek bağlamlarının derleme işlevinde doğru yerde bildirilmemesidir:

class ExampleEmptyBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
   $node = \Drupal::routeMatch()->getParameter('node');
   $build = array();

   if ($node) {
    $config = \Drupal::config('system.site');

    $build = array(
    '#type' => 'markup',
    '#markup' => '<p>' . $node->id() . '<p>',
    '#cache' => array(
      'tags' => $this->getCacheTags(),
      'contexts' => $this->getCacheContexts(),
    ),
   );
 }
 return $build;
 }
}

Bu bloğu düğüm olmayan bir düğümde çağırırsanız, build işlevi boş bir dizi döndürür, bu nedenle bu blok için önbellek içeriği olmaz ve bu davranış drupal tarafından önbelleğe alınır: bu bloğun görüntülenmesi geçersiz kılınmaz veya oluşturulmaz.

Çözüm sadece $ build'i her zaman önbellek bağlamıyla başlatmaktır:

class ExampleEmptyBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
   $node = \Drupal::routeMatch()->getParameter('node');

    $build = array(
    '#cache' => array(
      'tags' => $this->getCacheTags(),
      'contexts' => $this->getCacheContexts(),
    ),
   );

   if ($node) {
    $config = \Drupal::config('system.site');

    $build['#markup'] = '<p>' . $node->id() . '<p>';
    $build['#type'] = 'markup';
    }
 return $build;
 }
}

0

Bu konuşmaya geç kaldığımı fark ettim, ancak aşağıdaki kod benim için çalıştı:

class ExampleBlock extends BlockBase
{

  public function build()
  {
    $lcContent = '';

    $loNode = \Drupal::routeMatch()->getParameter('node');

    if (!$loNode)
    {
      return (array(
        '#type' => 'markup',
        '#cache' => array('max-age' => 0),
        '#markup' => $lcContent,
      ));
    }

    $lcContent .= "<div id='example_block' style='overflow: hidden; clear: both;'>\n";
    $lcContent .= $loNode->id();
    $lcContent .= "</div>\n";

    return (array(
      '#type' => 'markup',
      '#cache' => array('max-age' => 0),
      '#markup' => $lcContent,
    ));
  }
}

hiç geç olmamasından daha iyi :)
Alex

0

Hook_block_view_BASE_BLOCK_ID_alter uygulamayı denediniz mi?

işlev hook_block_view_BASE_BLOCK_ID_alter (dizi & $ yapı, \ Drupal \ Core \ Block \ BlockPluginInterface $ block) {$ build ['# cache'] ['max-age'] = 0; }

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.