Сортировка по цене каталога в поиске

Стандартный шаблон компонента поиска "Битрикса" представляет собой аккуратный, но не совсем подходящий интернет-магазину список результатов (кстати, hint: в последних версиях "Битрикса" появился красивый компонент поиска по каталогу - bitrix:catalog.search, который можно поставить на страница поиска - /search/index.php и это отчасти решит проблему).

Большинство наших клиентов просят переделать шаблон страницы поиска таким образом, чтобы он соответствовал шаблону каталога. При этом не составляет большого труда переделать сам вывод - легко получить все поля элемента каталога по ITEM_ID каждого элемента выходного массива поиска ($arResult['ITEMS'] и исправить шаблон в соответствии с шаблоном витрины (раздела каталога). Проблемы возникают, когда просят при этом реализовать также сортировку (например, по цене) и "умный" фильтр. Про фильтр - отдельная история, здесь же опишу простой подход, который позволяет выводить результаты поиска в нужном нам порядке, т.е. с правильной сортировкой.

Идея

Есть событие BeforeIndex, к которому можно привязать обработчик и в нем задавать необходимый вес поиска (CUSTOM_RANK) любому элементу каталога. Напишем функцию, которая будет вычислять CUSTOM_RANK в зависимости от наличия (от большего к меньшему, т.е. сначала идут товары, которых больше всего на складе) и в зависимости от цены (от меньшего к большему, т.е. дешевые товары идут первыми).

Реализация

В файле init.php добавим обработчик, который вызывается перед индексацией элемента каталога:
AddEventHandler("search", "BeforeIndex", "BeforeIndexHandler");
function BeforeIndexHandler($arFields) {
   if($arFields["MODULE_ID"] == "iblock" && $arFields["ITEM_ID"]) {
      CModule::IncludeModule('catalog');
      $arProduct = CCatalogProduct::GetByID($arFields["ITEM_ID"]);
      if($arProduct['QUANTITY'] <= 0)
         $k = 1;
      else
         $k = $arProduct['QUANTITY'] * 100;
      $dbPrice = CPrice::GetList(array(),array("PRODUCT_ID" => $arFields["ITEM_ID"],"CATALOG_GROUP_ID" => 2));
      $arPrice = $dbPrice->Fetch();
      
      $arFields['CUSTOM_RANK'] = $k * round(10000000/$arPrice['PRICE']);
   }
   return $arFields;
} 
Для товаров без остатка ($arProduct['QUANTITY'] <= 0) - задаем коэффициент веса = 1; для товаров на складе - кратно остатку на складе (в примере умножаем остаток на 100, но можно выбрать любое другое число, в зависимости от того, что важнее - выводить первыми товары с бОльшим остатком, или выводить более дешевые товары, но с меньшим остатком).

Итоговый вес для индексации - задаем прямо пропорциональным полученному коэффициенту и обратно пропорциональным цене товара.