Drupal 8/9: Facets AJAX commands

A while ago I was developing a website in Drupal that included a faceted search. The client had some very specific requirements which were not included in the facets module by default. One of those requirements was updating and adding several pieces of content on the page depending on which filters were activated. This might be an easy task if it weren’t for the AJAX functionality that’s embedded in the facets module…

Faceted search

Possible solutions

I came up with three possible solutions:

  • Listen to the facets_filter JavaScript event 😐
  • Alter the response object of the facet AJAX callback 👍

The JavaScript event

Implementing this solution would be pretty straight forward:

  • Add an event listener
  • Execute (jQuery) AJAX call(s) to the Drupal back-end
  • Update the content with for example jQuery(‘.selector’).html(response);

Altering the response object

After some researching I figured out that all facet AJAX requests were routed through one specific path:

/facets-block-ajax
  1. I only had to add some PHP code to make this work. Everything simple and centralized, just as I like my code to be.

The best way forward

To me, it was clear that the “Alter the response object of the facet AJAX callback” was by far the best approach. Now I just had to implement it… 💻

Overriding the facet route

I just registered a route subscriber in my services.yml file and added the subscriber:

Adding a custom controller

This controller extends the base FacetBlockAjaxController class which does all the heavy lifting. The only thing I added was an event dispatcher which dispatches a custom event to alter the response object. This allowed me to alter the AJAX commands attached to the response.

Defining a custom event

The next step was to define the custom FacetsAlterAjaxCommandsEvent event class. This class keeps track of the altered response object throughout the entire event subscriber process:

Adding event subscribers

At this point, my “mini framework” was ready to be put to work. I added several custom modules which all had an event subscriber:

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store