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…
I came up with three possible solutions:
- Disable AJAX on the faceted search 💩
- Alter the response object of the facet AJAX callback 👍
The first option was never really an option because the client required the faceted search to work without reloading the page after every change. The only two options left, needed some more in-depth analysis.
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:
That route had a controller as a callback that returned a simple AjaxResponse object with multiple AJAX commands attached to it:
Knowing Drupal I knew I could override this callback and add extra AJAX commands to the response.
This would mean that:
- I didn’t need an extra (unnecessary) request to the back-end
- 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… 💻
I divided the task at hand into four sub-tasks:
Overriding the facet route
I just registered a route subscriber in my services.yml file and added the subscriber:
At this point, the facet AJAX callback pointed to my custom controller.
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:
Important to keep in mind here is that in PHP, objects exhibit a “reference-like” behavior: While you can not assign a completely different value, you can still change the properties of the object.
This means we don’t need to set the altered response object for the changes to be picked up by the event dispatcher in the controller.
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:
Each of these event subscribers added an extra AJAX command which in turn were picked up by the dispatcher in my custom controller… ét voila, every facet filter change was accompanied by my custom HTML changes.
I feel confident this is a pretty good solution until the facets module provides us with some mechanism to achieve this more generically. Of course, this is not the only solution. Feel free to share your thoughts on this approach in the comment section!