Score:0

How should I use hook_cache_flush() or hook_rebuild() in a custom module to execute JavaScript code when the cache is cleared?

jp flag

I'm trying to create a module that executes some JavaScript code when the cache is flushed (via administrator menu or otherwise). I believe I can implement either hook_cache_flush() or hook_rebuild(), but I'm not quite sure how. I'm not necessarily looking for this to be done for me, but if I could be given a starting point and direction to head in, that'd be great.

Score:1
us flag

Neither hook_cache_flush() nor hook_rebuild() implementation can directly add JavaScript code to a page, simply for the fact they don't get any render array as argument, nor they return a render array to the function/method invoking them. You could add a form submission handler to the system_performance_settings form for when the Clear all caches button on that form is clicked. This won't work when the cache is cleared using other methods, for example by a module that calls drupal_flush_all_caches() in its code.

// Put the following line on the top of the file containing this code.
use Drupal\Core\Form\FormStateInterface;

function mymodule_form_system_performance_settings_alter(&$form, FormStateInterface $form_state) {
  if (isset($form['clear_cache']['clear'])) {
    if ($form_state->getTemporaryValue('mymodule_cache_cleared')) {
      $form['#attached']['library'][] = 'mymodule/cache.rebuild';
    }
    $form['clear_cache']['clear']['#submit'][] = 'mymodule_system_performance_settings_submit';
  }
}

function mymodule_system_performance_settings_submit(array &$form, FormStateInterface $form_state) {
  $form_state->setRebuild();
  $form_state->setTemporaryValue('mymodule_cache_cleared', TRUE);
}

For a solution that works in all the cases drupal_flush_all_caches() is called, and adds the JavaScript code to the first page requested after the cache is cleared, I would implement hook_cache_flush() to set a value that is then checked in hook_page_attachments().

function mymodule_cache_flush() {
  \Drupal::state()->set('mymodule_cache_cleared', TRUE);
}

function mymodule_page_attachments(array &$attachments) {
  $state = \Drupal::state();
  if ($state->get('mymodule_cache_cleared')) {
    // Delete the state value to avoid the library is added to
    // every page after the cache is cleared.
    $state->delete('mymodule_cache_cleared');

    $attachments['#attached']['library'][] = 'mymodule/cache.rebuild';
  }
}

To add the JavaScript code to the first X pages requested after the cache clear, I would use the following hook implementations. (The following code attaches the library to the first three page requests.)

function mymodule_cache_flush() {
  \Drupal::state()->set('mymodule_cache_cleared_count', 3);
}

function mymodule_page_attachments(array &$attachments) {
  $state = \Drupal::state();
  if ($count = $state->get('mymodule_cache_cleared_count')) {
    $attachments['#attached']['library'][] = 'mymodule/cache.rebuild';
    $state->set('mymodule_cache_cleared_count', $count - 1);
  }
}

I used \Drupal::state()->get() and \Drupal::state()->set() because:

  • The value set with drupal_static() won't be preserved between page requests. The documentation page clearly says:

    All functions requiring a static variable to persist or cache data within a single page request are encouraged to use this function unless it is absolutely certain that the static variable will not need to be reset during the page request.

  • Setting a session value, each user would get a different value. This means the JavaScript code would be added to the next page requested after the cache is cleared only when that page is requested from the same user who was logged in when the cache has been cleared.

Add JavaScript to Your Theme or Module shows how a JavaScript library is defined from a module.

jp flag
I wanted to use one of the functions I listed so that the code would execute every time one of those functions was called. That way, yes, the button in performance settings would do it, but also if someone had the admin menu module installed, and cleared their cache that way, it would still execute. Isn't there a way to execute JS when a certain hook is used, regardless of its arguments or what it returns?
apaderno avatar
us flag
The only hook that would be able to alter the render array for a page would be invoked for every page and it won't be able to understand whether the cache is cleared.
jp flag
I know which hook you're talking about and you're correct. As for the JS, I won't ask for how to do what I'm particularly looking to do, but if I wanted to add something simple like an alert just to test my library, could you give me a hint on how to do that? I read through the documentation you linked and didn't find a solution that worked for me.
cn flag
You could always set a static var in the hook and pick it up again in `hook_preprocess_page()` (see [`drupal_static()`](https://api.drupal.org/api/drupal/core%21includes%21bootstrap.inc/function/drupal_static/9.3.x) for the pattern to use). But it won't work in all contexts; for example when you clear cache from the admin menu, it redirects back to the page it came from, so you don't have an opportunity to attach libraries to the page. You could get around that by using session or temporary storage, but it's more effort and could get messy if a page redirect fails for example
4uk4 avatar
cn flag
@Clive, that's a good idea setting a session value, like you could set a message for the the next full page load. To read and clear this session value you can't use the page preprocess hook, though, pages are cached. You can use a response event subscriber after the page is retrieved from cache or an uncacheable block with cache max-age 0, which is always cached as placeholder and runs on every page request.
apaderno avatar
us flag
@Joseph I added a solution more, since what reported in the comments is not completely correct.
apaderno avatar
us flag
@4uk4 It's a good idea if the OP needs a different value for each user. If the OP needs to set a value when the cache is cleared and use that value to add JavaScript code to the next required page, independently from the user who accesses that page, the session cannot be used.
4uk4 avatar
cn flag
OK, but having the cache clear in a post request directly redirecting to a normal page load (then of course with the same session cookie) is the most likely use case of drupal_flush_all_caches() according to the docs. If there is a browser involved. Otherwise javascript doens't make much sense.
apaderno avatar
us flag
@4uk4 I don't understand either what the purpose of running JavaScript code when Drupal cache is cleared is. I would think the OP is trying to change something at browser level, but this is only a wild guess. That's why I avoided to say anything about whether using JavaScript is a good or bad idea.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.