Компонент "Умный фильтр" (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" шаблона мы можем разместить только одну строку для вывода всех элементов на главной странице раздела:
Код размещения фильтра для шаблона компонента "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") через:
Кроме того, чтобы фильтр мог использовать ЧПУ на главной странице компонента, понадобится копирование и несложная доработка самого комплексного компонента - "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, -1, PREG_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($smartPart, toLower("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($smartPart, toLower($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;
}
}
|
21.04.201818:0721.04.2018 18:07:49
неплохая статья
Как добавить два компонента фильтра? работает только один из них.
нужен фильтр по шинам и дискам, два разных.