Символьный код элемента и раздела - генерация и уникальность
Красивый адрес товара собирается из символьного кода, и пустой код ломает ссылку целиком. Включаем автогенерацию кода из названия, разводим совпадения и проставляем код на уже залитом каталоге.
Решение
Включаем генерацию кода в настройках инфоблока
Открываем настройки инфоблока, вкладка «Поля»:
Символьный код ДаЕсли код задан, то проверять на уникальность ДаТранслитерировать из названия при добавлении ДаМаксимальная длина результата транслитерации 100Приведение к регистру к нижнемуЗамена для символа пробела -Замена для прочих символов -Удалять лишние символы замены ДаВкладка «Поля разделов» настраивается отдельно теми же значениями: галочка у элементов на разделы не распространяется. Замена прочих символов дефисом убирает из адреса подчёркивания, с которыми поисковые системы работают хуже.
Дописываем код при записи из кода
Объектный ORM код сам не генерирует:
$elementClass = \Bitrix\Iblock\IblockTable::compileEntity('Catalog');$element = $elementClass::createObject() ->setName('Дрель ударная BX-500') ->setCode('drel-udarnaya-bx-500'); // код задаём явно$element->save();Механизмы старого ядра при записи через ORM выполняются вручную: генерация символьного кода, поисковая индексация, пересчёт вложенности разделов. Настройки инфоблока на объектную запись не действуют, поэтому пустой код после сохранения через ORM - ожидаемый результат.
Готовый код из названия отдаёт платформа:
$code = \CIBlockElement::generateMnemonicCode($name, $iblockId);if ($code === null) { // транслитерация выключена настройками $code = 'element-' . $elementId;}Метод возвращает null, когда в настройках инфоблока снята галочка
транслитерации или включён внешний сервис перевода. Запасное значение из
идентификатора спасает адрес от пустого куска пути, хотя человекопонятным такой
адрес уже не будет.
Проставляем код на залитом каталоге
Собираем занятые коды до первой записи:
$used = [];$res = \CIBlockElement::GetList(['ID' => 'ASC'], ['IBLOCK_ID' => 5], false, false, ['ID', 'NAME', 'CODE']);while ($row = $res->Fetch()) { $used[$row['CODE']] = true; // коды, уже занятые в этом инфоблоке}Без предварительного списка проверка уникальности отклонит половину элементов, и
скрипт молча пройдёт мимо них. Метод Fetch() данные не экранирует, поэтому
значения выборки на страницу не выводим.
Разводим совпадения числовым хвостом:
$base = \CIBlockElement::generateMnemonicCode($row['NAME'], 5);$code = $base;for ($i = 2; isset($used[$code]); $i++) { $code = $base . '-' . $i; // brelok, brelok-2, brelok-3}$used[$code] = true;$elementClass::update($row['ID'], ['CODE' => $code]);\CIBlockElement::UpdateSearch($row['ID']);Статический update() пишет только базовые поля - NAME, CODE, ACTIVE, - и
значения свойств не трогает. Поисковый индекс после объектной записи обновляется
отдельным вызовом, иначе товар с новым кодом пропадёт из поиска по сайту.
Правим коды разделов
Разделы обновляем своей скомпилированной сущностью:
$sectionClass = \Bitrix\Iblock\Model\Section::compileEntityByIblock('Catalog');$sectionClass::update($sectionId, ['CODE' => 'instrument']);// разделы для обхода читаем через \CIBlockSection::GetList()Код раздела становится отдельным куском пути, поэтому его уникальность важна не меньше кода элемента. Смена кода у раздела меняет адрес сразу всем товарам внутри, поэтому старые пути закрывают переадресацией.
Подставляем код в шаблон адреса
Шаблоны адресов лежат в настройках инфоблока:
URL страницы раздела #SITE_DIR#/catalog/#SECTION_CODE#/URL детальной страницы #SITE_DIR#/catalog/#SECTION_CODE#/#ELEMENT_CODE#/Макросы подставляет CIBlock::ReplaceDetailUrl() в момент сборки ссылки. Шаблон
с #ELEMENT_ID# вместо кода работает всегда, но адрес перестаёт быть
человекопонятным.
Проверяем результат
Считаем элементы, оставшиеся без кода:
$res = \CIBlockElement::GetList([], ['IBLOCK_ID' => 5], false, false, ['ID', 'CODE']);$empty = 0;while ($row = $res->Fetch()) { if ($row['CODE'] === '') { $empty++; }}printf("без кода: %d\n", $empty);Ноль в счётчике означает, что каждый элемент собирает собственный адрес. Дальше сбрасываем кэш витрины и открываем пару ссылок руками: страница из кэша показывает прежний адрес.
Типичные проблемы
Раньше код генерировался, теперь при добавлении новости он пустой.
В настройках инфоблока снята галочка «Транслитерировать из названия при добавлении элемента». Настройка живёт на вкладке «Поля» и после правок инфоблока часто теряется.
При импорте из CSV коды не проставились, хотя вручную всё работает.
Транслитерация из настроек инфоблока применяется не на всех путях записи. Пустые коды добирают своим обработчиком на событиях OnBeforeIBlockElementAdd и OnBeforeIBlockElementUpdate, подставляя значение полю кода.
После обновления ядра коды разделов сменились сами, ЧПУ разъехалось.
В настройках интеграции с 1С включились галочки транслитерации кода при добавлении и при обновлении. Каждый обмен переписывает код раздела заново из названия.
Из названия «Брелок - мороженое» получился код вида __22.
Латиница проходит транслитерацию, а кириллица заменяется символом замены. Причина в кодировке на пути к функции транслитерации, а не в настройках инфоблока.
Элемент записан объектным ORM, а кода у него нет.
Объектная запись не выполняет механизмы старого ядра, включая генерацию кода. Значение задают вызовом setCode() до сохранения.
Частые вопросы
Не работает генерация символьного кода, раньше всё работало. В чём причина?
Сначала смотрим вкладку «Поля» в настройках инфоблока: галочка транслитерации при добавлении элемента снимается при правке полей. Второе место - настройки интеграции с 1С, где живут собственные галочки транслитерации.
Можно ли передавать символьный код из 1С, а не генерировать на сайте?
Да, код приходит вместе с товаром обычным полем и записывается как есть. Тогда галочки транслитерации при добавлении и обновлении выключают, иначе обмен перепишет присланное значение своим.
Как проставить код всем уже загруженным товарам?
Скриптом: обходим инфоблок порциями, генерируем код из названия и пишем базовое поле через статический update(). Занятые коды держим в массиве, чтобы проверка уникальности не отклоняла записи.
Что делать, если два товара получили одинаковый код?
Включаем в настройках инфоблока проверку уникальности и разводим существующие совпадения числовым хвостом. До этого момента открывается тот товар, которого обработчик адреса нашёл первым.
Мешают ли подчёркивания в символьном коде?
Формально нет, адрес с ними работает. На практике их меняют на дефис: так задан разделитель в настройках транслитерации, и такой адрес привычнее и поисковым системам, и людям.
Смежное
- ЧПУ комплексного компонента - оглавление подтемы
- Компоненты 2.0 - устройство компонентов целиком
- ЧПУ комплексного компонента: настройка и разбор 404 - куда код попадает в шаблоне адреса
- Свои ЧПУ-адреса: правила обработки, порядок, конфликты - адреса вне шаблонов компонента
- Комплексный компонент изнутри: маршруты, переменные, выбор страницы - как код читается из адреса
- Ссылка на товар ведёт в 404 - разбор, когда код уже сломал адрес
- Массовая правка товаров: обновление свойств скриптом - обход каталога порциями
- Правка структуры разделов: перенос, слияние, удаление - что происходит с адресами при переносе
- Структура каталога при выгрузке из 1С - откуда берутся коды разделов при обмене
- Дубли страниц каталога: канонический адрес, пагинация, фильтр - лишние адреса одного товара