партнер компании 1с-битрикс
сайт фрилансера Сергея Эстрина
Войти как пользователь
Вы можете войти на сайт, если вы зарегистрированы на одном из этих сервисов:
Универсальная галерея - модуль для битрикс
Компонент "Умный фильтр" (catalog.smart.filter) - безусловно хороший и удобный, но если использовать его вне стандартного шаблона комплексного компонента "catalog", на первый взгляд может показаться, что он недоделан (например, отказывается нормально работать без указания раздела и др.), но это не совсем так. В этой статье я расскажу как использовать его скрытые возможности и как вносить некоторые изменения в ЧПУ фильтра.
[spoiler]
Использование фильтра без указания раздела

В стандартном шаблоне компонента "catalog" компонент "catalog.smart.filter" используется на странице раздела. В комплексном же компоненте "news" вообще используется устаревший компонент "catalog.filter", почему, лично для меня это остается загадкой - могу только предполагать, что либо компании "Битрикс" глубоко безразлично все, что не связано с электронной коммерцией, либо разработчики сами не до конца уверены в производительности "catalog.smart.filter" при обработке большого количества элементов. Так или иначе, использовать "catalog.smart.filter" в шаблоне компонента "news" или в том же "catalog", но не в конкретном разделе, не так сложно, основная проблема, которая тут возникает - это то, что по умолчанию, без указания в параметрах компонента ID или символьного кода раздела не работает счетчик количества элементов (показывает 0). Чтобы избавится от этой проблемы, укажем в параметре "SECTION_ID" - 0, а в дополнительном параметре "SHOW_ALL_WO_SECTION", который был заботливо подготвлен программистами, но потом почему-то тщательно спрятан от посторонних глаз - "Y". Напомню, что такой же параметр существует и у компонента "catalog.section" и означает "Показывать все элементы без указания раздела".

Код размещения фильтра для шаблона компонента "catalog":

$APPLICATION->IncludeComponent(

   
"bitrix:catalog.smart.filter",
   
"",
   array(
      
"IBLOCK_TYPE" => $arParams["IBLOCK_TYPE"],
      
"IBLOCK_ID" => $arParams["IBLOCK_ID"],
      
"SECTION_ID" => !!$arCurSection?$arCurSection['ID']:0,
      
"SHOW_ALL_WO_SECTION" => !!$arCurSection?"N":"Y"// !!!
      
"FILTER_NAME" => $arParams["FILTER_NAME"],
      
"PRICE_CODE" => $arParams["PRICE_CODE"],
      
"CACHE_TYPE" => $arParams["CACHE_TYPE"],
      
"CACHE_TIME" => $arParams["CACHE_TIME"],
      
"CACHE_GROUPS" => $arParams["CACHE_GROUPS"],
      
"SAVE_IN_SESSION" => "N",
      
"FILTER_VIEW_MODE" => $arParams["FILTER_VIEW_MODE"],
      
"XML_EXPORT" => "Y",
      
"SECTION_TITLE" => "NAME",
      
"SECTION_DESCRIPTION" => "DESCRIPTION",
      
'HIDE_NOT_AVAILABLE' => $arParams["HIDE_NOT_AVAILABLE"],
      
"TEMPLATE_THEME" => $arParams["TEMPLATE_THEME"],
      
'CONVERT_CURRENCY' => $arParams['CONVERT_CURRENCY'],
      
'CURRENCY_ID' => $arParams['CURRENCY_ID'],
      
"SEF_MODE" => $arParams["SEF_MODE"],
      
"SEF_RULE" => $arResult["FOLDER"].$arResult["URL_TEMPLATES"]["smart_filter"],
      
"SMART_FILTER_PATH" => $arResult["VARIABLES"]["SMART_FILTER_PATH"],
      
"PAGER_PARAMS_NAME" => $arParams["PAGER_PARAMS_NAME"],
      
"INSTANT_RELOAD" => $arParams["INSTANT_RELOAD"],
      
"DISPLAY_ELEMENT_COUNT" => "N",
   ),
   
$component,
   array(
'HIDE_ICONS' => 'Y')
);

К компоненту "catalog.section" также нужно добавить параметр:

"SHOW_ALL_WO_SECTION" => "Y",


Таким образом в файле "sections.php" шаблона мы можем разместить только одну строку для вывода всех элементов на главной странице раздела:

include('section.php');


Код размещения фильтра для шаблона компонента "news" (параметры связанные с каталогом, отключены):

$APPLICATION->IncludeComponent(

   
"bitrix:catalog.smart.filter",
   
"",
   array(
      
"IBLOCK_TYPE" => $arParams["IBLOCK_TYPE"],
      
"IBLOCK_ID" => $arParams["IBLOCK_ID"],
      
"SECTION_ID" => !!$arResult['VARIABLES']['SECTION_ID'] || !!$arResult['VARIABLES']['SECTION_CODE']?$arResult['VARIABLES']['SECTION_ID']:0,
      
"SECTION_CODE" => $arResult['VARIABLES']['SECTION_CODE'],
      
"SHOW_ALL_WO_SECTION" => "Y"// !!!
      
"FILTER_NAME" => $arParams["FILTER_NAME"],
      
"PRICE_CODE" => array(),
      
"CURRENCY_ID" => "",
      
"CONVERT_CURRENCY" => "N",
      
"HIDE_NOT_AVAILABLE" => "N",
      
"CACHE_TYPE" => $arParams["CACHE_TYPE"],
      
"CACHE_TIME" => $arParams["CACHE_TIME"],
      
"CACHE_GROUPS" => $arParams["CACHE_GROUPS"],
      
"SAVE_IN_SESSION" => "N",
      
"FILTER_VIEW_MODE" => "",
      
"XML_EXPORT" => "Y",
      
"SECTION_TITLE" => "NAME",
      
"SECTION_DESCRIPTION" => "DESCRIPTION",
      
"SEF_MODE" => $arParams["SEF_MODE"],
      
"SEF_RULE" => $arResult["FOLDER"].$arResult["SMART_FILTER_URL_TEMPLATE"],
      
"SMART_FILTER_PATH" => $arResult["VARIABLES"]["SMART_FILTER_PATH"],
      
"PAGER_PARAMS_NAME" => $arParams["PAGER_PARAMS_NAME"],
      
"DISPLAY_ELEMENT_COUNT" => "N",
      
"INSTANT_RELOAD" => "N",
   ),
   
$component,
   array(
'HIDE_ICONS' => 'Y')
);

Данный код также рассчитан на размещение в разделе и подключение страницы раздела на главной (в "news.php") через:

include('section.php');


Кроме того, чтобы фильтр мог использовать ЧПУ на главной странице компонента, понадобится копирование и несложная доработка самого комплексного компонента - "catalog" или "news":

Для компонента "catalog":

1. Добавляем строку

"smart_filter_index" => "filter/#SMART_FILTER_PATH#/apply/",


в массив $arDefaultUrlTemplates404 (если что-то не работает, попробуйте поменять в нем порядок элементов).

2. После вызова $engine->guessComponentPath добавляем строки:

if ($componentPage === "smart_filter_index")

   
$componentPage "sections";

Для компонента "news":

1. Перед определением массива добавляем строку:

$smartBase = ($arParams["SEF_URL_TEMPLATES"]["section"]? $arParams["SEF_URL_TEMPLATES"]["section"]: "#SECTION_ID#/");


2. В сам массив $arDefaultUrlTemplates404 добавляем строки (если что-то не работает, попробуйте поменять в нем порядок элементов):

"smart_filter" => $smartBase."filter/#SMART_FILTER_PATH#/apply/",

"smart_filter_index" => "filter/#SMART_FILTER_PATH#/apply/",

3. Там же где:

$engine->addGreedyPart("#SECTION_CODE_PATH#");

добавляем (как в компоненте "catalog"):

$engine->addGreedyPart("#SMART_FILTER_PATH#");


4. После вызова $engine->guessComponentPath добавляем строки:

if ($componentPage === "smart_filter")

   
$componentPage "section";

if (
$componentPage === "smart_filter_index")
   
$componentPage "sections";

Кастомизация ЧПУ умного фильтра

В компоненте "catalog.smart.filter" предусмотрено неплохое ЧПУ, но в некоторых случаях требуется и его доработка. Например, в строке ЧПУ при фильтрации по цене указывается символьный код типа цены. А с учетом того, что этот символьный код обычно идет из 1с и имеет странное русское название, то в строке ЧПУ мы получим такое: "/price-типовое_соглашение_для_сайта-from-2500/". Заказчик скорее всего не пройдет мимо этого и попросит убрать.

К счастью, компонент "catalog.smart.filter" сделан на классах и мы можем использовать наследование и переопределить только нужные функции. В указанном примере мы просто пренебрегаем типом цены и используем для фильтрации первый попавшийся тип цены из тех, что указаны в параметрах компонента (вы когда-нибудь видели сайт, где используется фильтрация по нескольким типам цен?).

Таким образом файл class.php примет вид (я так же включил в пример функцию encodeSmartParts для наглядности, хотя она не изменялась):

<?

use 
Bitrix\Main\Loader;

if(!
defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();

CBitrixComponent::includeComponentClass("bitrix:catalog.smart.filter");

class 
CCustomCatalogSmartFilter extends CBitrixCatalogSmartFilter
{
   public function 
searchPrice($items$lookupValue)
   {      
      foreach(
$items as $itemId => $arItem)
      {
         if (
$arItem["PRICE"])
         {
            
//просто убираем эти условия и используем первый попавшийся тип цены
            //$code = toLower($arItem["CODE"]);
            //if ($lookupValue === $code)
               
return $itemId;
         }
      }
      return 
false;
   }

   public function 
convertUrlToCheck($url)
   {
      
$result = array();
      
$smartParts explode("/"$url);
      foreach (
$smartParts as $smartPart)
      {
         
$item false;
         
$smartPart preg_split("/-(from|to|is|or)-/"$smartPart, -1PREG_SPLIT_DELIM_CAPTURE);
         foreach (
$smartPart as $i => $smartElement)
         {
            if (
$i == 0)
            {
               if (
preg_match("/^price$/"$smartElement$match)) // изменено, было: "/^price-(.+)$/"
                  
$itemId $this->searchPrice($this->arResult["ITEMS"], $match[1]);
               else
                  
$itemId $this->searchProperty($this->arResult["ITEMS"], $smartElement);

               if (
$itemId)
                  
$item = &$this->arResult["ITEMS"][$itemId];
               else
                  break;
            }
            elseif (
$smartElement === "from")
            {
               
$result[$item["VALUES"]["MIN"]["CONTROL_NAME"]] = $smartPart[$i+1];
            }
            elseif (
$smartElement === "to")
            {
               
$result[$item["VALUES"]["MAX"]["CONTROL_NAME"]] = $smartPart[$i+1];
            }
            elseif (
$smartElement === "is" || $smartElement === "or")
            {
               
$valueId $this->searchValue($item["VALUES"], $smartPart[$i+1]);
               if (
strlen($valueId))
               {
                  
$result[$item["VALUES"][$valueId]["CONTROL_NAME"]] = $item["VALUES"][$valueId]["HTML_VALUE"];
               }
            }
         }
         unset(
$item);
      }      
      return 
$result;
   }

   public function 
makeSmartUrl($url$apply$checkedControlId false)
   {
      
$smartParts = array();

      if (
$apply)
      {
         foreach(
$this->arResult["ITEMS"] as $PID => $arItem)
         {
            
$smartPart = array();
            
//Prices
            
if ($arItem["PRICE"])
            {
               if (
$arItem["VALUES"]["MIN"]["HTML_VALUE"] || $arItem["VALUES"]["MAX"]["HTML_VALUE"])
               {
                  if (
$arItem["VALUES"]["MIN"]["HTML_VALUE"])
                     
$smartPart["from"] = $arItem["VALUES"]["MIN"]["HTML_VALUE"];
                  if (
$arItem["VALUES"]["MAX"]["HTML_VALUE"])
                     
$smartPart["to"] = $arItem["VALUES"]["MAX"]["HTML_VALUE"];
               }
            }

            if (
$smartPart)
            {
               
array_unshift($smartParttoLower("price")); // изменено, было: toLower("price-".$arItem["CODE"])

               
$smartParts[] = $smartPart;
            }
         }

         foreach(
$this->arResult["ITEMS"] as $PID => $arItem)
         {
            
$smartPart = array();
            if (
$arItem["PRICE"])
               continue;

            
//Numbers && calendar == ranges
            
if (
               
$arItem["PROPERTY_TYPE"] == "N"
               
|| $arItem["DISPLAY_TYPE"] == "U"
            
)
            {
               if (
$arItem["VALUES"]["MIN"]["HTML_VALUE"] || $arItem["VALUES"]["MAX"]["HTML_VALUE"])
               {
                  if (
$arItem["VALUES"]["MIN"]["HTML_VALUE"])
                     
$smartPart["from"] = $arItem["VALUES"]["MIN"]["HTML_VALUE"];
                  if (
$arItem["VALUES"]["MAX"]["HTML_VALUE"])
                     
$smartPart["to"] = $arItem["VALUES"]["MAX"]["HTML_VALUE"];
               }
            }
            else
            {
               foreach(
$arItem["VALUES"] as $key => $ar)
               {
                  if (
                     (
                        
$ar["CHECKED"]
                        || 
$ar["CONTROL_ID"] === $checkedControlId
                     
)
                     && 
strlen($ar["URL_ID"])
                  )
                  {
                     
$smartPart[] = $ar["URL_ID"];
                  }
               }
            }

            if (
$smartPart)
            {
               if (
$arItem["CODE"])
                  
array_unshift($smartParttoLower($arItem["CODE"]));
               else
                  
array_unshift($smartPart$arItem["ID"]);

               
$smartParts[] = $smartPart;
            }
         }
      }

      if (!
$smartParts)
         
$smartParts[] = array("clear");

      return 
str_replace("#SMART_FILTER_PATH#"implode("/"$this->encodeSmartParts($smartParts)), $url);
   }

   public function 
encodeSmartParts($smartParts)
   {
      foreach (
$smartParts as &$smartPart)
      {
         
$urlPart "";
         foreach (
$smartPart as $i => $smartElement)
         {
            if (!
$urlPart)
               
$urlPart .= urlencode($smartElement);
            elseif (
$i == 'from' || $i == 'to')
               
$urlPart .= urlencode('-'.$i.'-'.$smartElement);
            elseif (
$i == 1)
               
$urlPart .= urlencode('-is-'.$smartElement);
            else
               
$urlPart .= urlencode('-or-'.$smartElement);
         }
         
$smartPart $urlPart;
      }
      unset(
$smartPart);
      return 
$smartParts;
   }

}

Чтобы оставить сообщение, авторизуйтесь, или войдите с помощью:
13.11.2018 12:17:09
спасибо
неплохая статья
22.01.2020 15:23:38
два фильтра на стрнанице
Как добавить два компонента фильтра? работает только один из них.
нужен фильтр по шинам и дискам, два разных.
22.01.2020 16:44:20
Полагаю, разное значение параметра FILTER_NAME?
22.01.2020 17:05:43
нет,слишком просто
14.01.2022 17:08:25
У меня на сайте фильтр в конце добавляет после applay get запрос я так понимаю,  с вопросительным знаком и набором параметров. Как можно это убрать, подскажите?