Угораздило меня однажды на один из подопечных сайтов (основанный на движке Joomla) прикрутить электронный магазин VirtueMart версии 2.6.0. Потом меня попросили изменить вывод списка товаров и прочие «фенечки-рюшечки». Ну и я отказываться не стал взял да и изменил скрипты, причём ничего не задокументировал (а менял там много всего). Народ остался доволен, а я как водица «сделал и забыл». Время шло. В какой-то момент народишко мой обнаружил, что на сайте не работает поиск по каталогу товаров. Точнее он работает, но ищет везде кроме «магазина». Опять я засел за этот несчастный сайт.
Первым делом было выяснено что стандартный поиск Jooml-ы для магазина не годится. Но это вопрос, как показалось, легко поправимый ибо у VirtueMart-а есть свой плагин поиска — «VM — Search in Shop». Включил я его и понял, что всё в этой жизни не так просто. Поиск как ничего не искал раньше так и продолжил ничего не искать. Сразу же вспомнилась фраза из кинофильма «Константин»: «Всегда есть какой-то подвох». Тем более выяснилось, что данная версия движка оказалась глючной и надо бы её обновить, но не тут то было. Как я уже упомянул я кучу всего правил в движке и ничего не задокументировал, потому простой путь для меня был закрыт.
Принялся думать дальше. Поиски в сети ничего не дали и, если честно, меня даже утомили, ибо по этой тематике материала немерено, а вот то что нужно нет. Плюнул и предпринял «финт ушами» — полез в движок ручками. День потратил на поиски того что происходит после того как пользователь решил что-то найти на сайте. После третьего вскипания мозга я наконец упёрся в то место «где собака порылась». Оно оказалось в файле ./administrator/components/com_virtuemart/modules/product.php, точнее сказать в одной из функций которую он содержит под названием sortSearchListQuery. Это собственно та самая функция которая отвечает за поиск по каталогу магазина. Её изучение тоже заняло некоторое время и в конечном итоге дало свой плоды. Оно привело меня к $this -> valid_search_fields. Это не что иное как свойство объекта MartModulProduct (в данном случае псевдоним this подразумевает именно его), имеющее тип массив строк и содержащее имена полей поиска.
Должен вернуться немного назад, чтобы рассказать о том как нашёл в чём собственно состоит ошибка. У упомянутого VirtueMart-а есть свои логи и вот в одном из них я нашёл постоянные жалобы о плохом запросе и сообщения, что о недоступном поле product_name. Проанализировал запрос и покопался в базе данных. Выяснил, что поле product_name почему-то оказалось в таблице joomla_virtuemart_products вместо таблицы joomla_virtuemart_products_ru_ru, на которую опирается поиск да и как я понял должен опираться весь движок (но почему-то это не так, причём дело не в моих предыдущих правках). Посмотрев ещё маленько на поисковый запрос понял, что в нём должно быть обращение к p.product_name где алиас p указывает на «новую» таблицу. Стало понятно, что необходимо вклиниться в процесс формирования запроса и сделать так, чтобы на выходе тот оказался правильным. А ещё лучше отрегулировать настройки, но это увы мне не удалось (я просто их не нашёл), потому пошёл по пути правки процесса формирования запроса. Согласен вариант не лучший, но рабочий.
Вернёмся к редактированию функции sortSearchListQuery. Как я уже сказал она использует для получения полей поиска свойство valid_search_fields. Нас интересует его нулевой элемент, именно в нём содержится наше «неправильное» поле.
Ещё одно небольшое отступление: в процессе изучения функции было выяснено, что значение в указанном массиве должно быть не p.product_name а `p`.product_name (символ ` — это не апостроф а символ находящийся на одной кнопке с «тильдой» и буквой ё), и именно так иначе в процессе разных преобразований и передаче значений между переменными получается то же самое, что было до исправления т.е. «неправильный» запрос.
Итак для того, чтобы завершить наш «мерзкий и коварный план взлома запроса», необходимо разместить конструкцию $this->valid_search_fields[0] = ‘`p`.product_name’ внутри функции sortSearchListQuery упомянутого скрипта. Я не увидел для этого лучшего места чем перед циклом который отправляет этот массив дальше по цепочке переменных. Если быть точным то непосредственно перед строкой: foreach($this->valid_search_fields as $searchField). Таким образом задуманный «мерзкий план» был осуществлён и поиск заработал так как нужно.
P.S. Описанный случай касается ситуации когда поиск происходит по множеству полей включающему в себя поле «Название товара» (product_name) иначе все эти изменения бессмысленны. Список включённых для поиска полей можно найти зайдя в админку сайта, выбрав в меню Настройки VirtueMart и пройдя по пути Настройки -> Вкладка «Настройки сортировки» -> группа «доступные поля для поиска», там же этот список можно и изменить.
P.S.S. Я описал случай для версии 2.6.0 магазина VirtueMart, на другой версии возможно всё совсем иначе, а скорее всего этой ошибки нет вообще.
UPD.: Как водиться: «Хорошая мысля приходи апасля». Во избежание того, что вдруг кто-то исключил поле product_name из множества полей поиска и для добавления, тем самым, гибкости скрипту, лучше использовать конструкцию:
if (strcmp($this->valid_search_fields[0], 'product_name')
{
$this->valid_search_fields[0] = '`p`.product_name';
}
вместо:
$this->valid_search_fields[0] = '`p`.product_name';