मैं वर्तमान नोड के आधार पर सामग्री दिखाने वाले अपने कस्टम ब्लॉक के लिए कैशिंग को सही तरीके से कैसे सेट करूं?


19

मेरे पास यह बहुत बुनियादी ब्लॉक है जो सिर्फ वर्तमान नोड की आईडी दिखाता है।

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

}

लेकिन एक बार कैश हो जाने के बाद, ब्लॉक वही रहता है, चाहे मैं किस नोड पर जाऊं। मैं नोड आईडी प्रति परिणाम को सही ढंग से कैश कैसे करूं?


1
देखो पर getCacheTags()BlockBase से, तुम बस जरूरत एक टैग है कि आपके नोड का प्रतिनिधित्व जोड़ने (नोड: {एनआईडी})। क्षमा करें, अब मैं जल्दी में हूं, मैं बाद में बेहतर समझा सकता हूं,
वैगनर

जवाबों:


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 नामक फ़ाइल में रखें, इसके नामस्थान {मॉड्यूल_नाम} को बदलें, कैश को साफ़ करें और इसका उपयोग करें।


इसलिए ट्रिक #cacheबिल्ड फंक्शन में सेटिंग्स को हटाने और सार्वजनिक कार्यों को जोड़ने के लिए है?
एलेक्स

3
इससे कोई फर्क नहीं पड़ता कि आप कैश टैग और संदर्भ कहां सेट करते हैं।
4k4

खैर, मुझे लगता है कि यह अधिक समझ में आता है, क्योंकि हम एक ब्लॉक का निर्माण कर रहे हैं, इसलिए ब्लॉक को कैश करने की आवश्यकता है। यदि आप भविष्य में अपने ब्लॉक को बदलते हैं (यानी कुछ अतिरिक्त रेंडर तत्व डालते हैं), तो आपका ब्लॉक काम करेगा।
वागनर

@ 4k4 url.path को लगता है कि उन्होंने भी काम किया है। क्या फर्क पड़ता है?
एलेक्स

2
@ वैगनर: कैश टैग / कॉन्टेक्ट्स को रेंडर सरणी में रखना भी एक बुरा विचार नहीं है, क्योंकि आपके पास यह है कि आपका डेटा कहां है, जो इस पर निर्भर है। और यह हमेशा बुलबुला होगा, इसलिए आपको उन तत्वों के बारे में चिंता करने की ज़रूरत नहीं है जो ऊपर हैं। Btw। आपका कोड बहुत अच्छा है, कैशिंग मुद्दों को बहुत अच्छी तरह से समझाता है।
4k4

13

ऐसा करने का सबसे आसान तरीका प्लगइन / ब्लॉक संदर्भ प्रणाली पर भरोसा करना है।

मेरा उत्तर देखें कि मैं वर्तमान ब्लॉक सामग्री को खींचने वाले ब्लॉक को कैसे बनाते हैं?

आपको इस तरह से अपने ब्लॉक एनोटेशन में एक नोड संदर्भ परिभाषा डालनी होगी:

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

और फिर इसे इस तरह उपयोग करें: $this->getContextValue('node')

इसके बारे में अच्छी बात यह है कि द्रुपाल आपके लिए कैशिंग का ध्यान रखेगा। खुद ब खुद। क्योंकि यह जानता है कि डिफ़ॉल्ट (और जहां तक ​​कोर केवल जाती है) नोड संदर्भ वर्तमान नोड है। और यह जानता है कि यह कहां से आ रहा है, इसलिए कैश संदर्भ और कैश टैग स्वचालित रूप से जुड़ जाते हैं।

\Drupal\Core\Plugin\ContextAwarePluginBase::getCacheContexts()संबंधित getCacheTags()विधियों के माध्यम से , ब्लॉकबेस / आपकी ब्लॉक क्लास उस से फैली हुई है और उन तरीकों को विरासत में मिली है।


आप के \Drupal::routeMatch()->getParameter('node')साथ प्रतिस्थापित करते हैं $this->getContextValue('node')और आप कोड की एक पंक्ति के साथ पूरे कैशिंग मुद्दे को हल करते हैं? महान!
4k4

1
अब तक धन्यवाद! क्या आप एक पूर्ण कोड उदाहरण प्रदान कर सकते हैं?
एलेक्स

@ एलेक्स: मैंने आपका प्रश्न संपादित किया। यदि आपको कोई त्रुटि मिलती है तो कृपया कोड को जांचें और बदलें।
4k4

@ 4k4 मैंने इसे नहीं आजमाया क्योंकि दूसरा समाधान भी काम करता है
एलेक्स

@ एलेक्स - पूर्ण कोड उदाहरण: 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कक्षा का उपयोग करने जा रहे हैं, तो फ़ाइल के शीर्ष पर जोड़ना याद रखें ।


धन्यवाद, लेकिन / नोड / 2 पर ब्लॉक अभी भी 1 आउटपुट करता है जब मैंने अपने कैश को साफ़ करने के बाद नोड / 1 का दौरा किया,
एलेक्स

2
यदि आप किसी मॉड्यूल को सक्षम कर रहे हैं, तो आपको इसे संपादित करने से पहले इसे अनइंस्टॉल करना होगा। कैश को साफ़ करना पर्याप्त नहीं है।
kiamlaluno

ठीक है, लेकिन अधिकतम 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 () प्रतिस्थापन नहीं हैं। उन्हें केवल अतिरिक्त कैश मेटाडेटा के लिए उपयोग किया जाना चाहिए, जिसे रेंडर सरणी के माध्यम से वितरित नहीं किया जा सकता है।

प्रलेखन देखें:

"यह अत्यंत महत्त्वपूर्ण है कि आप रेंडर एपीआई की रेंडर एपीआई को रेंडर एरे की सूचना दें।"

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

इस उदाहरण को देखें कि कैसे ड्रुपल ऑटो-प्लेसहोल्डरिंग और आलसी-बिल्डिंग के माध्यम से कैशिंग का अनुकूलन करते समय आवश्यक कैश मेटाडेटा प्रदान करने के लिए एक रेंडर सरणी की उम्मीद करता है। समस्या को उपयोगकर्ता संदर्भ के साथ कस्टम ब्लॉक पर उपयोगकर्ता के विशिष्ट कैश टैग सेट करना


मुझे नहीं लगता कि यह ब्लॉक ऑब्जेक्ट का कैश सेट कर सकता है। '#markup' सिर्फ एक रेंडर एलिमेंट ऑब्जेक्ट है और इसमें कैश संदर्भ या टैग सेट करने का कोई कारण नहीं है। कैश ब्लॉक होने पर ब्लॉक ऑब्जेक्ट को फिर से बनाना पड़ता है।
वैग्नर

#markupकिसी अन्य रेंडर तत्व के समान कैश किया जा सकता है। इस मामले में यह मार्कअप नहीं है, लेकिन ब्लॉक, जो कैश किया गया है और यहां समस्या है। आप इसे कैश टैग के साथ हल नहीं कर सकते, क्योंकि वे केवल अमान्य हो जाते हैं, अगर नोड डेटाबेस में बदल जाता है।
4k4

@ वैगनर आप किसी ब्लॉक ऑब्जेक्ट का कैश सेट कर सकते हैं; BlockBaseवर्ग भी आवश्यक तरीकों है।
kiamlaluno

1
मेरे return [ '#markup' => render($output), '#cache' => [ 'contexts' => ['url'] ] ];लिए प्रति URL कैशिंग के लिए सुपर फाइन काम करता है।
लीमैनक्स

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

यदि आप उस ब्लॉक को एक गैर नोड पर कॉल करते हैं, तो बिल्ड फ़ंक्शन एक खाली सरणी लौटाता है, इसलिए इस ब्लॉक के लिए कोई कैश संदर्भ नहीं है और यह व्यवहार ड्रुपल द्वारा कैश किया जाएगा: इस ब्लॉक का प्रदर्शन ठीक से अमान्य नहीं होगा या प्रदान नहीं किया जाएगा।

समाधान सिर्फ $ बिल्ड कैश संदर्भों के साथ आरंभ करने के लिए है:

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 (सरणी और $ बिल्ड, \ Drupal \ Core \ Block \ BlockPluginInterface $ ब्लॉक) {$ बिल्ड ['# कैश'] ['अधिकतम आयु]] = 0; }

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.