Стандартный компонент bitrix:search.title (строка поиска с выпадающими подсказками) — удобный инструмент, но по умолчанию он ищет только по заголовкам элементов инфоблоков. Этого часто бывает недостаточно, когда ключевая информация хранится в пользовательских свойствах: артикулах, брендах, цветах, странах производителя. А если у вас товар с торговыми предложениями (разные размеры, цвета), то поиск по заголовку основного товара и вовсе теряет многие полезные запросы пользователей.
В этой статье я покажу, как «научить» компонент bitrix:search.title искать:
-
по одному или нескольким пользовательским свойствам элемента;
-
по свойствам типа «Список»;
-
по данным из торговых предложений (SKU).
Всё это реализуется через событие BeforeIndex без единого изменения ядра или стандартных шаблонов.
Как это работает
Перед тем как запись попадает в поисковый индекс, модуль поиска генерирует событие BeforeIndex. Мы можем перехватить его и изменить поле TITLE — именно по нему происходит поиск в компоненте bitrix:search.title. Добавляя в TITLE дополнительные данные (значения свойств, названия предложений, артикулы), мы включаем их в область поиска.
Всё, что требуется — создать обработчик события и один раз перестроить индекс.
Базовый вариант: поиск по одному свойству
Начнём с простого примера. Допустим, у вас есть свойство ARTICLE (артикул), и вы хотите, чтобы товар находили по его значению.
Шаг 1. Добавляем обработчик в init.php
Откройте файл /local/php_interface/init.php (если его нет — создайте) и добавьте код:
<?php
AddEventHandler('search', 'BeforeIndex', 'BeforeIndexHandler');
function BeforeIndexHandler($arFields)
{
if (!CModule::IncludeModule('iblock')) {
return $arFields;
}
if ($arFields['MODULE_ID'] == 'iblock') {
$propertyValue = CIBlockElement::GetProperty(
$arFields['PARAM2'], // ID инфоблока
$arFields['ITEM_ID'], // ID элемента
['sort' => 'asc'],
['CODE' => 'ARTICLE'] // символьный код свойства
);
if ($prop = $propertyValue->Fetch()) {
$arFields['TITLE'] .= ' ' . $prop['VALUE'];
}
}
return $arFields;
}Шаг 2. Переиндексация поиска
Зайдите в административную панель:
https://ваш-сайт.ру/bitrix/admin/search_reindex.php?lang=ru
-
Снимите галочку «Переиндексировать только измененные документы».
-
Нажмите «Переиндексировать».
Шаг 3. Проверка
Теперь при вводе артикула в поисковую строку товар будет найден, даже если артикул отсутствует в заголовке.
Расширенный вариант: несколько свойств и торговые предложения
В реальных проектах обычно требуется больше гибкости. Рассмотрим ситуацию:
-
У товара есть свойства «Бренд», «Цвет», «Страна».
-
Товар имеет торговые предложения (SKU) с собственными артикулами и названиями (например, «Кроссовки 41 размер»).
-
Нужно, чтобы поиск находил товар по любому из этих параметров.
Полный код обработчика
<?php
// /local/php_interface/init.php
AddEventHandler('search', 'BeforeIndex', 'BeforeIndexHandler');
function BeforeIndexHandler($arFields)
{
if (!CModule::IncludeModule('iblock')) {
return $arFields;
}
if ($arFields['MODULE_ID'] !== 'iblock') {
return $arFields;
}
$additionalData = [];
// ------------------------------------------------------------
// 1. Несколько свойств самого элемента
// ------------------------------------------------------------
$propertyCodes = ['ARTICLE', 'BRAND', 'COLOR', 'COUNTRY']; // укажите свои коды
$resProps = CIBlockElement::GetProperty(
$arFields['PARAM2'],
$arFields['ITEM_ID'],
'sort' => 'asc',
['CODE' => $propertyCodes]
);
while ($prop = $resProps->Fetch()) {
if (empty($prop['VALUE'])) {
continue;
}
// Для свойств типа "Список" берём текст, а не ID
$value = ($prop['PROPERTY_TYPE'] == 'L')
? $prop['VALUE_ENUM']
: $prop['VALUE'];
$additionalData[] = $value;
}
// ------------------------------------------------------------
// 2. Торговые предложения (SKU)
// ------------------------------------------------------------
$offersIblockId = getOffersIblockId($arFields['PARAM2']);
if ($offersIblockId) {
$offers = CIBlockElement::GetList(
[],
[
'IBLOCK_ID' => $offersIblockId,
'PROPERTY_CML2_LINK' => $arFields['ITEM_ID'],
],
false,
['nTopCount' => 30], // ограничиваем количество для производительности
['ID', 'NAME', 'IBLOCK_ID']
);
while ($offer = $offers->Fetch()) {
// Добавляем название предложения
$additionalData[] = $offer['NAME'];
// Добавляем свойства предложения (например, артикул варианта)
$offerProps = CIBlockElement::GetProperty(
$offersIblockId,
$offer['ID'],
'sort' => 'asc',
['CODE' => 'ARTICLE_OFFER'] // свойство для предложений
);
while ($offerProp = $offerProps->Fetch()) {
if (!empty($offerProp['VALUE'])) {
$additionalData[] = $offerProp['VALUE'];
}
}
}
}
// ------------------------------------------------------------
// 3. Объединяем всё и добавляем к заголовку
// ------------------------------------------------------------
if (!empty($additionalData)) {
$uniqueData = array_unique($additionalData);
$arFields['TITLE'] .= ' ' . implode(' ', $uniqueData);
}
return $arFields;
}
/**
* Возвращает ID инфоблока торговых предложений для обычного инфоблока
*/
function getOffersIblockId($catalogIblockId)
{
if (!\Bitrix\Main\Loader::includeModule('catalog')) {
return null;
}
$catalogInfo = \Bitrix\Catalog\CatalogIblockTable::getList([
'select' => ['PRODUCT_IBLOCK_ID'],
'filter' => ['=IBLOCK_ID' => $catalogIblockId],
])->fetch();
return $catalogInfo['PRODUCT_IBLOCK_ID'] ?? null;
}Что здесь происходит
-
Несколько свойств — мы передаём массив кодов в
CIBlockElement::GetPropertyи получаем все значения за один запрос. -
Свойства-списки — определяем тип
'L'и используемVALUE_ENUMвместоVALUE. -
Торговые предложения — находим инфоблок SKU через
CatalogIblockTable, затем для каждого предложения берём его название и дополнительные свойства. -
Удаляем дубли — с помощью
array_uniqueизбегаем многократного повторения одинаковых слов в индексе. -
Ограничение количества предложений — параметр
nTopCountзащищает от падения производительности, если у товара сотни вариантов.
Пример работы
Представим товар в каталоге:
-
Название товара: «Кроссовки Nike Air Max»
-
Свойства:
BRAND = Nike,COLOR = Белый,COUNTRY = Вьетнам -
Предложение 1: «Размер 41», артикул
NK-AM-41 -
Предложение 2: «Размер 42», артикул
NK-AM-42
После индексации поле TITLE будет выглядеть так:
Кроссовки Nike Air Max Nike Белый Вьетнам Размер 41 NK-AM-41 Размер 42 NK-AM-42Теперь пользователь найдёт этот товар по запросам:
-
Nike -
Белый -
Вьетнам -
41 -
NK-AM-41 -
42 размер
Оптимизация для больших каталогов
Если у вас тысячи товаров и у каждого десятки предложений, перебор всех предложений при индексации может стать проблемой. Вот несколько решений:
1. Сократите количество индексируемых предложений
['nTopCount' => 20] // максимум 20 предложений на товар2. Индексируйте только уникальные ключевые слова
Вместо добавления всех названий предложений, можно добавить только уникальные характеристики:
$sizes = [];
while ($offer = $offers->Fetch()) {
// Извлекаем размер из названия
if (preg_match('/размер (\d+)/i', $offer['NAME'], $matches)) {
$sizes[] = $matches[1];
}
}
if ($sizes) {
$additionalData[] = 'размеры: ' . implode(',', array_unique($sizes));
}3. Используйте GetList для свойств вместо множественных GetProperty
$elem = CIBlockElement::GetList(
[],
['ID' => $arFields['ITEM_ID']],
false,
false,
['ID', 'PROPERTY_ARTICLE', 'PROPERTY_BRAND', 'PROPERTY_COLOR']
)->Fetch();
$additionalData[] = $elem['PROPERTY_ARTICLE_VALUE'];
$additionalData[] = $elem['PROPERTY_BRAND_VALUE'];
$additionalData[] = $elem['PROPERTY_COLOR_VALUE'];Этот способ делает один запрос к БД вместо нескольких.
Важные замечания
-
Переиндексация обязательна после любого изменения кода обработчика. Без неё старые записи в индексе не обновятся.
-
Не все типы свойств подходят. Свойства типа «Файл», «Дата», «Привязка к пользователям» бессмысленно добавлять в текст. Ограничьтесь строками, списками и числами.
-
Влияние на размер индекса. Чем больше данных вы добавите в
TITLE, тем тяжелее будет индекс. Добавляйте только релевантную для поиска информацию. -
Многоязычность. Если сайт многоязычный, свойство может хранить значения на разных языках. Учитывайте
$arFields['LID']при выборке. -
Сложные свойства. Для свойств типа «Привязка к элементам» нужно дополнительно получать заголовки связанных элементов через
CIBlockElement::GetByID.
Заключение
Мы расширили компонент bitrix:search.title, добавив в него поиск по:
-
одному и нескольким пользовательским свойствам;
-
свойствам-спискам;
-
названиям и свойствам торговых предложений.
Всё это сделано через событие BeforeIndex, без модификации ядерных файлов и стандартных компонентов. Такой подход:
-
безопасен при обновлениях платформы;
-
легко настраивается под конкретные задачи;
-
работает «из коробки» с любым компонентом, использующим поисковый индекс (не только
search.title).
Теперь ваш поиск станет действительно полезным для клиентов: они смогут находить товары по артикулам, брендам, цветам, размерам — любой информации, которую вы сочтёте важной.