0

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.

1 Answer 1

1

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.

9
  • 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?
    – Joseph
    Mar 25, 2022 at 17:53
  • 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.
    – apaderno
    Mar 25, 2022 at 17:58
  • 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.
    – Joseph
    Mar 25, 2022 at 18:22
  • You could always set a static var in the hook and pick it up again in hook_preprocess_page() (see drupal_static() 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
    – Clive
    Mar 25, 2022 at 20:55
  • 1
    @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.
    – 4uk4
    Mar 25, 2022 at 22:07

Your Answer

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

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