Символьный код — то, из чего складывается адрес страницы, а значит вещь долгоживущая: сгенерировали неудачно, а исправлять придётся вместе с настройкой редиректов. Разберём, как генерировать коды правильно с первого раза, и почему автогенерация «на лету» опаснее, чем кажется.
Зачем нужен символьный код
Символьный код (CODE) элемента используется в ЧПУ (человекопонятных URL):
/catalog/smartfony/вместо/catalog/?SECTION_ID=15/product/iphone-15-pro/вместо/product/?ID=12345
Код должен быть уникальным, содержать только латиницу, цифры и дефисы.
Метод 1: CUtil::translit (рекомендуемый)
Встроенная функция Битрикса для транслитерации:
<?php
use Bitrix\Main\Loader;
Loader::includeModule('iblock');
$name = 'iPhone 15 Pro Max 256GB Синий титан';
$params = [
'replace_space' => '-', // Замена пробелов
'replace_other' => '-', // Замена прочих символов
'delete_repeat_replace' => true, // Удалять повторы дефисов
'use_google' => false, // Не использовать Google Translate
];
$code = \CUtil::translit($name, 'ru', $params);
// Результат: iphone-15-pro-max-256gb-sinii-titan
// Или краткая версия
$code = \CUtil::translit($name, 'ru', ['replace_space' => '-', 'replace_other' => '-']);use_google => false — правильное значение, и менять его не стоит. Параметр включает перевод названия через внешний сервис вместо транслитерации. Это означает сетевой запрос в момент сохранения элемента (то есть замедление админки и обмена с 1С), зависимость от доступности стороннего API и непредсказуемый результат: одно и то же название сегодня и завтра может дать разные коды, а значит — разные URL.
Добавление элемента с автогенерацией кода
<?php
use Bitrix\Main\Loader;
Loader::includeModule('iblock');
$name = 'Смартфон Samsung Galaxy S24 Ultra';
// Генерируем символьный код
$code = \CUtil::translit(
mb_strtolower($name),
'ru',
[
'replace_space' => '-',
'replace_other' => '-',
'delete_repeat_replace' => true,
]
);
$el = new \CIBlockElement();
$elementId = $el->Add([
'IBLOCK_ID' => 2,
'NAME' => $name,
'CODE' => $code,
'ACTIVE' => 'Y',
'PREVIEW_TEXT' => 'Флагманский смартфон Samsung',
'PROPERTY_VALUES' => [
'PRICE' => 129990,
'VENDOR' => 'Samsung',
],
]);
if ($elementId) {
echo "Добавлен элемент ID: {$elementId}, CODE: {$code}";
} else {
echo "Ошибка: " . $el->LAST_ERROR;
}Класс для генерации уникального кода
<?php
namespace Local\Iblock;
class CodeGenerator
{
private array $translitParams = [
'replace_space' => '-',
'replace_other' => '-',
'delete_repeat_replace' => true,
];
/**
* Генерация кода из строки
*/
public function generate(string $name): string
{
$code = \CUtil::translit(
mb_strtolower(trim($name)),
'ru',
$this->translitParams
);
// Удаляем дефисы в начале и конце
return trim($code, '-');
}
/**
* Генерация уникального кода для инфоблока
*/
public function generateUnique(string $name, int $iblockId, ?int $excludeId = null): string
{
$baseCode = $this->generate($name);
$code = $baseCode;
$suffix = 1;
while ($this->codeExists($code, $iblockId, $excludeId)) {
$code = $baseCode . '-' . $suffix;
$suffix++;
// Защита от бесконечного цикла
if ($suffix > 1000) {
$code = $baseCode . '-' . uniqid();
break;
}
}
return $code;
}
/**
* Проверка существования кода
*/
private function codeExists(string $code, int $iblockId, ?int $excludeId): bool
{
$filter = [
'IBLOCK_ID' => $iblockId,
'=CODE' => $code,
];
if ($excludeId) {
$filter['!ID'] = $excludeId;
}
$result = \CIBlockElement::GetList(
[],
$filter,
false,
['nTopCount' => 1],
['ID']
);
return (bool) $result->Fetch();
}
/**
* Генерация кода для раздела
*/
public function generateForSection(string $name, int $iblockId, ?int $excludeId = null): string
{
$baseCode = $this->generate($name);
$code = $baseCode;
$suffix = 1;
while ($this->sectionCodeExists($code, $iblockId, $excludeId)) {
$code = $baseCode . '-' . $suffix;
$suffix++;
}
return $code;
}
private function sectionCodeExists(string $code, int $iblockId, ?int $excludeId): bool
{
$filter = [
'IBLOCK_ID' => $iblockId,
'=CODE' => $code,
];
if ($excludeId) {
$filter['!ID'] = $excludeId;
}
$result = \CIBlockSection::GetList(
[],
$filter,
false,
['ID']
);
return (bool) $result->Fetch();
}
}
// Использование
$generator = new CodeGenerator();
// Простая генерация
$code = $generator->generate('iPhone 15 Pro');
// iphone-15-pro
// Уникальный код
$code = $generator->generateUnique('iPhone 15 Pro', 2);
// iphone-15-pro или iphone-15-pro-1 если уже занятАвтоматическая генерация через событие
<?php
// /local/php_interface/init.php
use Bitrix\Main\EventManager;
use Local\Iblock\CodeGenerator;
// Автогенерация CODE при добавлении
EventManager::getInstance()->addEventHandler(
'iblock',
'OnBeforeIBlockElementAdd',
function (&$arFields) {
// Только если код не указан
if (!empty($arFields['CODE'])) {
return;
}
// Только для определённых инфоблоков
$autoCodeIblocks = [2, 3, 5]; // ID инфоблоков
if (!in_array($arFields['IBLOCK_ID'], $autoCodeIblocks)) {
return;
}
$generator = new CodeGenerator();
$arFields['CODE'] = $generator->generateUnique(
$arFields['NAME'],
$arFields['IBLOCK_ID']
);
}
);
// Обновление CODE при изменении NAME
EventManager::getInstance()->addEventHandler(
'iblock',
'OnBeforeIBlockElementUpdate',
function (&$arFields) {
// Пропускаем если CODE явно передан
if (isset($arFields['CODE'])) {
return;
}
// Проверяем изменилось ли имя
$element = \CIBlockElement::GetByID($arFields['ID'])->Fetch();
if (!$element || $element['NAME'] === $arFields['NAME']) {
return;
}
// Генерируем новый код
$generator = new CodeGenerator();
$arFields['CODE'] = $generator->generateUnique(
$arFields['NAME'],
$arFields['IBLOCK_ID'],
$arFields['ID']
);
}
);Настройки транслитерации в админке
Битрикс позволяет настроить автогенерацию кода в настройках инфоблока:
- Перейдите в Контент → Инфоблоки → Типы инфоблоков
- Выберите нужный инфоблок
- Вкладка Поля → Символьный код
- Установите флаг «Транслитерировать из названия при добавлении»
- Настройте параметры транслитерации
Массовая генерация кодов
Скрипт для генерации кодов у существующих элементов:
<?php
$_SERVER['DOCUMENT_ROOT'] = realpath(__DIR__ . '/../../');
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
use Bitrix\Main\Loader;
use Local\Iblock\CodeGenerator;
Loader::includeModule('iblock');
$iblockId = 2;
$generator = new CodeGenerator();
$result = \CIBlockElement::GetList(
['ID' => 'ASC'],
[
'IBLOCK_ID' => $iblockId,
['LOGIC' => 'OR', '=CODE' => '', '=CODE' => false],
],
false,
false,
['ID', 'NAME', 'CODE']
);
$updated = 0;
while ($element = $result->Fetch()) {
if (!empty($element['CODE'])) {
continue;
}
$code = $generator->generateUnique($element['NAME'], $iblockId, $element['ID']);
$el = new \CIBlockElement();
if ($el->Update($element['ID'], ['CODE' => $code])) {
echo "ID: {$element['ID']} => {$code}\n";
$updated++;
}
}
echo "\nОбновлено элементов: {$updated}\n";Массовая генерация кодов меняет адреса всех страниц каталога. Это не техническая операция, а SEO-событие: старые URL начинают отдавать 404, позиции сбрасываются, внешние ссылки ведут в никуда, а реклама с прямыми ссылками на карточки перестаёт работать.
Если запускаете скрипт из этой статьи на живом проекте:
- Сохраните соответствие старых и новых кодов до обновления — потом восстановить его будет не из чего.
- Настройте 301-редиректы со старых адресов на новые.
- Обновите sitemap и отправьте его на переобход.
- Делайте это разом, а не порциями в течение недели: поисковик должен увидеть единовременное изменение структуры, а не постоянно меняющийся сайт.
И самое главное — не генерируйте коды заново для элементов, у которых они уже есть. В скрипте для этого есть проверка; убирать её ради «единообразия» не стоит ни при каких обстоятельствах.
Итоги
Код должен быть неизменяемым после публикации. Автогенерация уместна на OnBeforeIBlockElementAdd, но не на Update: переименование товара не должно менять его адрес. Иначе каждая правка названия контент-менеджером создаёт новую страницу и убивает старую.
Проверка уникальности обязательна, но помните о её стоимости: это запрос к базе на каждое сохранение, и на массовом импорте он множится на объём каталога.
Короткий код лучше длинного — /iphone-15-pro/ вместо /smartfon-apple-iphone-15-pro-max-256gb-sinii-titan/. Но не любой ценой: код обязан оставаться уникальным, а на каталоге с похожими названиями агрессивное сокращение приводит к суффиксам -2, -3, которые выглядят хуже длинного, но осмысленного адреса.
Транслитерация — только встроенная. Внешние переводчики дают недетерминированный результат, а недетерминированность в том, что становится URL, — это гарантированные проблемы.
Про уже существующие «плохие» коды. Соблазн переписать их одним скриптом велик, а выгода почти всегда меньше издержек: адреса вида /element-1234/ некрасивы, но работают и проиндексированы. Менять массово стоит, только если коды реально ломают работу (дубли, кириллица, недопустимые символы), — и тогда по процедуре с редиректами выше.
Комментарии
Система комментариев скоро будет подключена. А пока вы можете написать мне в Telegram или на email.