Некоторое время назад я разрабатывал сайт на Друпале с фасетным поиском. У клиента были очень специфические требования, которые не были включены в модуль фасетов по ​​умолчанию. Одним из таких требований было обновление и добавление нескольких частей контента на страницу в зависимости от того, какие фильтры были активированы. Это могло бы быть простой задачей, если бы не функциональность AJAX, встроенная в модуль фасетов…

Возможные решения

Я придумал три возможных решения:

  • Отключить AJAX в фасетном поиске 💩
  • Прослушайте событие JavaScript facets_filter 😐
  • Изменить объект ответа фасетного обратного вызова AJAX 👍

Первый вариант на самом деле никогда не был вариантом, потому что клиент требовал, чтобы фасетный поиск работал без перезагрузки страницы после каждого изменения. Осталось только два варианта, требующих более глубокого анализа.

Событие JavaScript

Реализация этого решения будет довольно простой:

  • Добавьте файл JavaScript
  • Добавить прослушиватель событий
  • Выполнение (jQuery) вызовов AJAX к серверной части Drupal
  • Обновите содержимое, например, с помощью jQuery(‘.selector’).html(response);

Однако этот подход требовал дополнительного (ненужного) запроса к серверной части, что замедляло процесс. Мне также пришлось бы создать какую-то конечную точку, чтобы запрос JavaScript AJAX мог извлекать данные. Что, в свою очередь, будет означать, что это решение будет зависеть от функциональности, написанной на JavaScript (внешняя часть) и PHP (внутренняя часть). У меня было забавное ощущение, что это не идеальный подход.

Изменение объекта ответа

После некоторых исследований я понял, что все фасетные AJAX-запросы направлялись по одному определенному пути:

/facets-block-ajax

Этот маршрут имел контроллер в качестве обратного вызова, который возвращал простой объект AjaxResponse с несколькими прикрепленными к нему командами AJAX:

Зная Drupal, я знал, что могу переопределить этот обратный вызов и добавить в ответ дополнительные команды AJAX.

Это будет означать, что:

  1. Мне не понадобился лишний (ненужный) запрос к бэкенду
  2. Мне нужно было только добавить немного PHP-кода, чтобы все заработало. Все просто и централизовано, как мне нравится, чтобы мой код был.

Лучший путь вперед

Для меня было ясно, что «Изменить объект ответа фасетного обратного вызова AJAX» на сегодняшний день является лучшим подходом. Теперь мне оставалось только реализовать это… 💻

Я разделил поставленную задачу на четыре подзадачи:

Переопределение фасетного маршрута

Я только что зарегистрировал подписчика маршрута в моем файле services.yml и добавил подписчика:

В этот момент обратный вызов AJAX фасета указывал на мой пользовательский контроллер.

Добавление пользовательского контроллера

Этот контроллер расширяет базовый класс FacetBlockAjaxController, который выполняет всю тяжелую работу. Единственное, что я добавил, — это диспетчер событий, который отправляет пользовательское событие для изменения объекта ответа. Это позволило мне изменить команды AJAX, прикрепленные к ответу.

Определение пользовательского события

Следующим шагом было определение пользовательского класса событий FacetsAlterAjaxCommandsEvent. Этот класс отслеживает измененный объект ответа на протяжении всего процесса подписки на события:

Здесь важно помнить, что в PHP объекты ведут себя как ссылки: Хотя вы не можете присвоить совершенно другое значение, вы все равно можете изменить свойства объекта.

Это означает, что нам не нужно устанавливать измененный объект ответа для того, чтобы изменения были получены диспетчером событий в контроллере.

Добавление подписчиков на события

К этому моменту мой «мини-фреймворк» был готов к работе. Я добавил несколько пользовательских модулей, у каждого из которых был подписчик на событие:

Каждый из этих подписчиков событий добавлял дополнительную команду AJAX, которая, в свою очередь, подхватывалась диспетчером в моем пользовательском контроллере… и вуаля, каждое изменение фасетного фильтра сопровождалось моими пользовательскими изменениями HTML.

Я уверен, что это довольно хорошее решение, пока модуль фасетов не предоставит нам какой-то механизм для достижения этого в более общем виде. Конечно, это не единственное решение. Не стесняйтесь делиться своими мыслями об этом подходе в разделе комментариев!