ฉันจะตั้งค่าการแคชอย่างถูกต้องสำหรับบล็อกที่กำหนดเองของฉันที่แสดงเนื้อหาขึ้นอยู่กับโหนดปัจจุบันได้อย่างไร


19

ฉันมีบล็อกพื้นฐานมากซึ่งเพิ่งแสดง ID ของโหนดปัจจุบัน

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

}

แต่เมื่อแคชบล็อกยังคงเหมือนเดิมโดยไม่คำนึงถึงโหนดที่ฉันเข้าชม ฉันจะแคชผลลัพธ์ต่อ ID โหนดได้อย่างถูกต้องได้อย่างไร


1
ดูgetCacheTags()จาก BlockBase คุณเพียงแค่ต้องเพิ่มแท็กที่แสดงถึงโหนดของคุณ (โหนด: {nid}) ขอโทษฉันรีบตอนนี้ฉันสามารถอธิบายได้ดีขึ้นในภายหลัง
Vagner

คำตอบ:


31

นี่คือรหัสการทำงานเต็มรูปแบบที่มีความคิดเห็น

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

ฉันทดสอบมัน มันได้ผล.

เพียงแค่ใส่รหัสในไฟล์ชื่อ NodeCachedBlock.php ในโฟลเดอร์โมดูลของคุณเปลี่ยนเนมสเปซ {module_name} ล้างแคชและใช้งาน


ดังนั้นเคล็ดลับคือการลบการ#cacheตั้งค่าในฟังก์ชั่นการสร้างและเพียงแค่เพิ่มฟังก์ชั่นสาธารณะ?
อเล็กซ์

3
ไม่สำคัญว่าคุณจะตั้งค่าแท็กแคชและบริบทอย่างไร
4k4

ฉันคิดว่ามันสมเหตุสมผลกว่าเพราะเรากำลังสร้างบล็อกดังนั้นบล็อกต้องถูกแคช หากคุณเปลี่ยนบล็อกของคุณในอนาคต (เช่นเพิ่มองค์ประกอบการแสดงผลเพิ่มเติม) บล็อกของคุณจะทำงาน
Vagner

@ 4k4 url.path ดูเหมือนว่าจะทำงานเช่นกัน ความแตกต่างคืออะไร?
อเล็กซ์

2
@Vagner: การวางแท็กแคช / บริบทในเรนเดอร์เรนเดอร์ก็ไม่ใช่ความคิดที่แย่เช่นกันเพราะคุณมีที่ซึ่งข้อมูลของคุณอยู่ซึ่งขึ้นอยู่กับมัน และมันจะทำให้เกิดฟองขึ้นเสมอดังนั้นคุณไม่ต้องกังวลกับองค์ประกอบที่อยู่ด้านบน Btw รหัสของคุณดีมากอธิบายปัญหาการแคชได้ดีมาก
4k4

13

วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการพึ่งพาระบบบริบทปลั๊กอิน / บล็อก

ดูคำตอบของฉันสำหรับฉันจะสร้างบล็อกที่ดึงเนื้อหาโหนดปัจจุบันได้อย่างไร

คุณต้องใส่นิยามบริบทของโหนดในหมายเหตุประกอบบล็อกของคุณเช่นนี้:

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

แล้วใช้มันเช่นนี้ $this->getContextValue('node')

สิ่งที่ดีเกี่ยวกับเรื่องนี้ก็คือ Drupal จะดูแลแคชให้คุณ อัตโนมัติ เพราะมันรู้ว่าบริบทโหนดเริ่มต้น (และเท่าที่แกนกลางเท่านั้น) เป็นโหนดปัจจุบัน และนั่นจะรู้ว่ามาจากไหนดังนั้นบริบทแคชและแท็กแคชจะถูกเพิ่มโดยอัตโนมัติ

ผ่าน\Drupal\Core\Plugin\ContextAwarePluginBase::getCacheContexts()และgetCacheTags()วิธีการที่สอดคล้องกันBlockBase / คลาสบล็อกของคุณขยายจากนั้นและสืบทอดวิธีเหล่านั้น


คุณแทนที่\Drupal::routeMatch()->getParameter('node')ด้วย$this->getContextValue('node')และคุณแก้ปัญหาแคชทั้งหมดด้วยหนึ่งบรรทัดของรหัส? ที่ดี!
4k4

1
ขอบคุณมาก! คุณสามารถให้ตัวอย่างรหัสแบบเต็มได้ไหม
อเล็กซ์

@Alex: ฉันแก้ไขคำถามของคุณ โปรดตรวจสอบและเปลี่ยนรหัสหากคุณพบข้อผิดพลาด
4k4

@ 4k4 ฉันไม่ได้ลองเพราะโซลูชันอื่นใช้ได้เช่นกัน
อเล็กซ์

@Alex - ตัวอย่างโค้ดแบบเต็ม: drupal.stackexchange.com/a/205155/15055
leymannx

7

หากคุณได้รับคลาสของปลั๊กอินบล็อกของDrupal\Core\Block\BlockBaseคุณคุณจะมีสองวิธีในการตั้งค่าแท็กแคชและบริบท

  • getCacheTags()
  • getCacheContexts()

ตัวอย่างเช่นบล็อกโมดูลหนังสือใช้วิธีการดังต่อไปนี้

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

บล็อกโมดูลฟอรัมใช้รหัสต่อไปนี้

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

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

ในกรณีของคุณฉันจะใช้รหัสต่อไปนี้

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

คุณสามารถใช้วิธีการต่อไปนี้เพื่อทำให้บล็อกไม่สามารถเข้าถึงได้ทั้งหมด (แม้ว่าฉันจะหลีกเลี่ยง) มันอาจจะมีประโยชน์ในกรณีอื่น ๆ อาจจะ

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

อย่าลืมเพิ่มuse Drupal\Core\Cache\Cache;ที่ด้านบนของไฟล์ถ้าคุณจะใช้Cacheคลาส


ขอบคุณ แต่บน / node / 2 บล็อกยังคงส่งออก 1 เมื่อฉันไปที่โหนด / 1 ในสถานที่แรกหลังจากล้างแคชของฉัน
Alex

2
หากคุณกำลังแก้ไขโมดูลที่เปิดใช้งานคุณจะต้องถอนการติดตั้งครั้งแรกก่อนที่จะแก้ไข การล้างแคชไม่เพียงพอ
kiamlaluno

โอเค แต่เพิ่ม maxAge 0 งานแปลกแล้ว!
อเล็กซ์

นอกจากนี้คลาสบล็อกของคุณใช้BlockBaseคลาสเป็นคลาสพาเรนต์ด้วยหรือไม่
kiamlaluno

ใช่มันใช้ได้
อเล็กซ์

3

เมื่อคุณสร้างอาร์เรย์เรนเดอร์ให้แนบเมทาดาทาที่ถูกต้องเสมอ:

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

นี่ไม่ใช่การบล็อกที่เฉพาะเจาะจงและวิธีการบล็อกแคชปลั๊กอินการพึ่งพา getCacheTags (), getCacheContext () และ getCacheMaxAge () ไม่ใช่การแทนที่ ควรใช้ข้อมูลเหล่านี้สำหรับข้อมูลเมตาแคชเพิ่มเติมเท่านั้นซึ่งไม่สามารถส่งผ่านอาร์เรย์การแสดงผลได้

ดูเอกสารประกอบ:

"เป็นสิ่งสำคัญอย่างยิ่งที่คุณต้องแจ้ง Render API ถึงความสามารถในการแคชของอาเรย์การเรนเดอร์"

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

ดูตัวอย่างนี้ Drupal คาดหวังว่าอาเรย์การเรนเดอร์จะให้ข้อมูลเมตาแคชที่จำเป็นเมื่อปรับการแคชผ่านการวางตำแหน่งอัตโนมัติและการสร้างสันหลังยาวปัญหาการตั้งค่าแท็กแคชเฉพาะของผู้ใช้ในบล็อกที่กำหนดเอง


ฉันไม่คิดว่าสามารถตั้งค่าแคชของวัตถุที่ถูกบล็อก '#markup' เป็นเพียงวัตถุองค์ประกอบ Render และไม่มีเหตุผลในการตั้งค่าบริบทหรือแท็กแคช วัตถุบล็อกที่ต้องสร้างใหม่เมื่อแคชไม่ถูกต้อง
Vagner

#markupสามารถแคชได้เช่นเดียวกับองค์ประกอบการแสดงผลอื่น ๆ ในกรณีนี้มันไม่ใช่มาร์กอัพ แต่บล็อกซึ่งแคชและนี่คือปัญหา คุณไม่สามารถแก้ไขได้ด้วยแท็กแคชเพราะจะได้รับการตรวจสอบความถูกต้องเฉพาะถ้ามีการเปลี่ยนแปลงโหนดในฐานข้อมูล
4k4

@Vagner คุณสามารถตั้งค่าแคชของวัตถุที่ถูกบล็อก; BlockBaseชั้นจะมีแม้กระทั่งวิธีการที่จำเป็น
kiamlaluno

1
สำหรับฉันใช้return [ '#markup' => render($output), '#cache' => [ 'contexts' => ['url'] ] ];งานได้ดีสุด ๆ สำหรับการแคช URL ต่อครั้ง
leymannx

1
ใช่ @leymannx มันง่ายอย่างนี้ ดูเหมือนว่าเธรดนี้จะพิจารณาปัญหานี้มากเกินไป
4k4

0

ปัญหาที่นี่คือบริบทของแคชจะไม่ถูกประกาศในตำแหน่งที่ถูกต้องในฟังก์ชั่นการสร้าง:

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

หากคุณเรียกใช้บล็อกนั้นบนโหนดที่ไม่ใช่ฟังก์ชันการสร้างจะส่งกลับอาร์เรย์ว่างดังนั้นจึงไม่มีบริบทแคชสำหรับบล็อกนี้และพฤติกรรมนั้นจะถูกแคชโดย drupal: การแสดงบล็อกนี้จะไม่ถูกต้องหรือแสดงผลไม่ถูกต้อง

โซลูชันเป็นเพียงการเริ่มต้น $ build ด้วยบริบทแคชทุกครั้ง:

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

ฉันรู้ว่าฉันมาสายการสนทนานี้ แต่รหัสด้านล่างใช้ได้สำหรับฉัน:

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,
    ));
  }
}

ดีกว่าไม่สาย :)
อเล็กซ์

0

คุณลองใช้ hook_block_view_BASE_BLOCK_ID_alter แล้วหรือยัง

function hook_block_view_BASE_BLOCK_ID_alter (อาร์เรย์ & $ build, \ Drupal \ Core \ Block \ BlockPluginInterface $ block) {$ build ['# cache'] ['max-age'] = 0; }

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.