4

Drupal 8:

I have the code for a block successfully doing its work and returning it. The block is called "RacerProfile" and it dumps all its content into variable "$pageContent". At the very end it returns the #markup. Ok. Now how do I tell this to invalidate every six hours?

/**
 * Provides a 'Racer Profile' Block
 *
 * @Block(
 *   id = "racer_profile",
 *   admin_label = @Translation("Slider v1 block")
 * )
 */
class RacerProfile extends BlockBase {
  /**
   * {@inheritdoc}
   */
  public function build() 
    {
    // does all the work to make $pageContent via non-drupal database queries 
    return array('#markup' => $this->t($pageContent),  );
    }
 }

In other Drupal answers, I've seen "D8 has cache tags and contexts that will automatically invalidate the block if something changes." Ok, my code is checking a second database. There's no way for Drupal to know what's changing over there. (Without checking, obviously.) So how do I add cache tags with specified timeouts? I can't find examples.

Notes:

-Obviously, this is not related to the Maximum Cache age within the admin area of Configuration > Performance > Caching > Page cache maximum age. I don't want this to apply to the entire site, just this block.

-Obviously, this is not related to settings.php in any way either, since again I don't want to have this apply to the entire site, and the various modules will have different timeout requirements.

-The traffic for the site is relatively low volume, so if rendering a thing four times a day even it only needs to be done once a day isn't a problem. Every six hours means that it's changed before people wake up across four time zones. Mostly. ;)

-(edit addition) The block needs to have its cache controlled for anonymous users.

3 Answers 3

3

This is all done from the render array itself. The nitty gritty details are documented in Cacheability of render arrays.

In your case, you need to do something like

return [
  '#markup' => $this->t($pageContent),
  '#cache' => [
    'max-age' => 86400, // one day in seconds
  ],
];

This uses the #cache entry for the render array, and sets the max-age to be one day. This is where you would also add in your cache contexts.

As a side note, try to avoid using '#markup'. Use themeable elements, either though code or Twig.

11
  • 1
    Note that #max-age doesn't apply to the anonymous page cache. Your only easy option there is to disable that completely on those pages with "\Drupal::service('page_cache_kill_switch')->trigger();". +1 to using proper #type and #theme render elements, not one big #markup. Especially don't just put all of that through t().
    – Berdir
    May 24, 2016 at 20:24
  • My use case is 100% for anonymous users. Apologies for not mentioning this. But to disable caching entirely on the HOME PAGE for a dynamic block to work properly for anonymous users seems crazy, doesn't it? May 24, 2016 at 20:27
  • #markup is then not the same as Drupal 7; in particular, it will get some of the tag removed. And yes, t() should be never be used for dynamic string that are not even predictable (such as, the name of the months).
    – apaderno
    May 24, 2016 at 20:28
  • Instead of t(), one would use??? May 24, 2016 at 20:38
  • Translate the separate original strings, e..g table headings and so on. Translating a big dynamic thing that is different all the time is pointless, untranslatable and fills up your locale table.
    – Berdir
    May 25, 2016 at 6:15
0

For cache settings of the dynamic cache see the answer from @MPD .

For the whole page you can set a global max-age in admin/config/development/performance.

The parameter is "Page cache maximum age". This is a bit misleading, because the Internal Page Cache does not respect this parameter *), but only puts this value in the response header for reverse proxies and browser caches.

For a high traffic site I would recommend to disable the Internal Page Cache (but keep the dynamic cache!) and use varnish, which will respect the max-age header and provides fine tuning for further handling of the headers.

Edit:

*) The reason for this behavior is, that Internal Page Cache uses Cache::PERMANENT, if the expire date is in the past:

$date = $response->getExpires()->getTimestamp();
$expire = ($date > time()) ? $date : Cache::PERMANENT;

And this expire date is set to "9-Nov-1978" by FinishResponseSubscriber:

 /**
   * Disable caching in ancient browsers and for HTTP/1.0 proxies and clients.
   *
   * HTTP/1.0 proxies do not support the Vary header, so prevent any caching by
   * sending an Expires date in the past. HTTP/1.1 clients ignore the Expires
   * header if a Cache-Control: max-age= directive is specified (see RFC 2616,
   * section 14.9.3).
   *
   * @param \Symfony\Component\HttpFoundation\Response $response
   *   A response object.
   */
  protected function setExpiresNoCache(Response $response) {
    $response->setExpires(\DateTime::createFromFormat('j-M-Y H:i:s T', '19-Nov-1978 05:00:00 UTC'));
  }
0

Confirming with previous comments in the thread that the best (if not easiest) way to make Drupal's Internal Page Cache "respect" the cache lifetime is to create an Event Subscriber that modifies the FinishResponseSubscriber shown above.

See boilerplate code for doing so at Anonymous user cache control

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.