8 lines
19 KiB
JSON
8 lines
19 KiB
JSON
{
|
||
"index": 362,
|
||
"slug": "bitrix-api-функция-для-генерации-кода-элемент",
|
||
"title": "Bitrix API: как сгенерировать символьный код элемента и не сломать URL",
|
||
"excerpt": "CUtil::translit создаёт только кандидата для CODE. Разбираем нормализацию, проверку уникальности, обновление элемента и безопасное поведение при смене публичного адреса.",
|
||
"contentHtml": "<p>В админке создают два элемента с названием «Телефон Samsung», а оба получают один символьный код <code>telefon-samsung</code>. Один URL начинает открывать не ту карточку или выборка возвращает первый найденный элемент. При массовом импорте ошибка повторяется сотни раз. Цена ошибки — дубли адресов, неверные карточки в каталоге и ручное восстановление старых ссылок.</p>\n<p>Другой симптом появляется позже. Редактор меняет название товара, код пересчитывается автоматически, а внешняя ссылка на прежний адрес отвечает 404. Здесь проблема не в качестве транслитерации. Публичный адрес связали с изменяемым полем без правила миграции.</p>\n<p>Тезис простой: <code>CUtil::translit</code> решает преобразование строки. Функция не знает, какие коды заняты, в каком инфоблоке ищут элемент и должен ли старый URL продолжать работать. Надёжный генератор делает четыре шага: получает кандидат, нормализует его, проверяет уникальность в нужной области и только потом сохраняет код. Для опубликованного элемента смена кода становится отдельной операцией с перенаправлением или таблицей старых адресов.</p>\n<h2>Что возвращает CUtil::translit</h2>\n<p>Метод принимает исходную строку, язык и массив параметров. В учебном примере язык задан как <code>ru</code>, регистр меняется на нижний, пробелы и прочие неподходящие символы заменяются дефисом, а повторяющиеся замены удаляются. Параметр <code>max_len</code> ограничивает длину результата.</p>\n<p>Эти параметры создают читаемую строку, но не делают её идентификатором. Названия «Кофе Classic 250 г» и «Кофе Classic 250г» могут после преобразования оказаться одинаковыми. Строка из одних знаков пунктуации может превратиться в пустое значение. Одинаковые названия в разных разделах могут быть допустимыми для редактора, но не для URL, если маршрут не содержит раздел.</p>\n<p>Символьный код нужно рассматривать как данные элемента. Название может измениться из-за исправления опечатки, локализации или требований каталога. Если URL строится из <code>CODE</code>, смена поля меняет внешний адрес. Поэтому генерация при создании и пересчёт при каждом редактировании — разные сценарии.</p>\n<figure><img src=\"/assets/illustrations/bitrix-translit-api.svg\" alt=\"Поток создания символьного кода: название, транслитерация, проверка уникальности и публичный URL\" /><figcaption>Транслитерация формирует кандидата. Решение о сохранении принимает код, который знает инфоблок, текущий ID и правило адреса.</figcaption></figure>\n<h2>Кандидат, нормализация и уникальность</h2>\n<p>Сначала задайте область уникальности. Если компонент ищет элемент по <code>IBLOCK_ID</code> и <code>CODE</code>, проверка должна использовать тот же инфоблок. Если код входит в глобальный адрес без раздела, безопаснее считать его уникальным для всей витрины. Если URL содержит <code>SECTION_CODE</code>, проект может разрешить одинаковый код в разных разделах, но это решение должно совпадать с фильтром компонента.</p>\n<p>При редактировании текущий элемент нельзя считать конфликтом с самим собой. Фильтр проверки исключает его <code>ID</code>. Иначе любое сохранение существующей записи будет добавлять новый суффикс, а код начнёт расти от <code>item</code> до <code>item-2</code> и дальше.</p>\n<p>После транслитерации уберите повторные дефисы и дефисы по краям. Пустой результат замените на понятный резервный префикс. В этом примере используется <code>item</code>. Реальный префикс выбирайте по доменной модели, а не по случайному значению.</p>\n<h2>Симптом → причина → проверка → действие</h2>\n<div class=\"table-scroll\"><table><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Причина</th><th scope=\"col\">Проверка</th><th scope=\"col\">Действие</th></tr></thead><tbody><tr><td>Две карточки имеют один <code>CODE</code></td><td>Проверили только транслитерацию</td><td>Посчитать элементы по инфоблоку и коду</td><td>Добавлять суффикс до сохранения и исправить конфликтующие записи</td></tr><tr><td>При редактировании код меняется сам</td><td>Генератор запускается для любого сохранения</td><td>Сравнить старый и новый код и режим операции</td><td>Генерировать автоматически только при создании или при пустом поле</td></tr><tr><td>Код пустой или начинается с дефиса</td><td>Название не содержит подходящих символов</td><td>Проверить результат после <code>trim</code> и регулярного выражения</td><td>Применить явный резервный префикс и проверить его уникальность</td></tr><tr><td>Код свободен при проверке, но запись конфликтует позже</td><td>Две операции выполняются параллельно</td><td>Повторить создание одновременно и проверить базу</td><td>Добавить ограничение или транзакционную стратегию, затем обработать ошибку записи</td></tr><tr><td>Старый адрес отвечает 404</td><td>Опубликованный <code>CODE</code> изменили без миграции</td><td>Открыть старый URL и проверить правила маршрута</td><td>Сохранить историю адресов и настроить редирект либо не менять код</td></tr></tbody></table></div>\n<h2>Учебная реализация</h2>\n<p>Ниже приведён учебный пример для старого API Bitrix. Функция <code>codeExists</code> намеренно обозначает адаптер проекта: в нём нужно выполнить выборку через <code>CIBlockElement::GetList</code> или другой принятый в проекте слой доступа. Пример не утверждает, что конкретная схема фильтра подходит каждому инфоблоку.</p>\n<pre><code><?php\n\nfunction makeElementCode($name, $iblockId, $elementId = 0)\n{\n $base = CUtil::translit((string)$name, 'ru', array(\n 'max_len' => 80,\n 'change_case' => 'L',\n 'replace_space' => '-',\n 'replace_other' => '-',\n 'delete_repeat_replace' => true,\n ));\n\n $base = trim(preg_replace('/-+/', '-', $base), '-');\n $base = $base !== '' ? $base : 'item';\n $code = $base;\n $suffix = 1;\n\n while (codeExists($iblockId, $code, $elementId)) {\n $suffix++;\n $code = $base . '-' . $suffix;\n }\n\n return $code;\n}\n\nfunction codeExists($iblockId, $code, $elementId = 0)\n{\n $filter = array(\n 'IBLOCK_ID' => (int)$iblockId,\n '=CODE' => $code,\n '!ID' => (int)$elementId,\n );\n\n $result = CIBlockElement::GetList(\n array(),\n $filter,\n false,\n array('nTopCount' => 1),\n array('ID')\n );\n\n return (bool)$result->Fetch();\n}</code></pre>\n<p>Код использует одинаковый контекст для генерации и проверки: тот же инфоблок, тот же формат <code>CODE</code> и исключение текущего ID. Если первый кандидат занят, цикл создаёт <code>telefon-samsung-2</code>, затем <code>telefon-samsung-3</code>. Номер не доказывает, что товары связаны между собой. Он только различает значения.</p>\n<p>Учебный код не выполняет запись элемента. В рабочем обработчике сначала проверьте обязательные поля и права, затем передайте полученный код в массив полей для <code>CIBlockElement::Add</code> или <code>Update</code>. После вызова проверьте возвращённый ID и ошибку API. Сохранение без проверки результата превращает конфликт в тихую потерю данных.</p>\n<p>Для обновления существующего элемента передавайте его ID в <code>makeElementCode</code>. Если код уже установлен и название просто изменилось, лучше оставить старое значение. Автоматический пересчёт нужен только при создании, при пустом поле или по отдельной подтверждённой команде. Это защищает опубликованные адреса от случайного изменения.</p>\n<h2>Как проверить элемент после записи</h2>\n<p>Сохранённая строка ещё не доказывает, что каталог откроет нужную карточку. Компонент может фильтровать по активности, разделу, сайту или дополнительному свойству. Проверка должна повторить путь чтения: найти элемент в том же инфоблоке по тому же <code>CODE</code>, затем открыть фактический URL.</p>\n<p>Если маршрут использует ЧПУ, адрес проходит через правила обработки URL. <code>CODE</code> может стать одной из переменных шаблона, но сам метод транслитерации не настраивает <code>urlrewrite.php</code>. Поэтому изменение генератора не исправит неверный <code>SEF_FOLDER</code>, порядок правил или фильтр компонента.</p>\n<h2>Порядок действий</h2>\n<ol><li>Зафиксировать, где используется код: фильтр инфоблока, шаблон ЧПУ, API, импорт или внешние ссылки.</li><li>Определить область уникальности: инфоблок, раздел, сайт или вся витрина.</li><li>Получить кандидата через <code>CUtil::translit</code> с явными параметрами языка, длины, регистра и замен.</li><li>Сжать повторные дефисы, убрать дефисы по краям и обработать пустой результат.</li><li>Проверить кандидат в выбранной области и исключить ID редактируемого элемента.</li><li>При конфликте добавлять суффикс и проверять каждую новую строку тем же запросом.</li><li>Сохранить код через API Bitrix, проверить ID и сообщение об ошибке, затем перечитать элемент.</li><li>Собрать URL тем же шаблоном, которым пользуется витрина, и открыть его на тестовых данных.</li><li>Для уже опубликованного кода отдельно решить судьбу старого адреса: редирект, история ссылок или запрет изменения.</li></ol>\n<h2>Отрицательный путь и границы решения</h2>\n<p>Проверка существования перед записью не устраняет гонку. Два параллельных запроса могут увидеть свободный <code>telefon-samsung</code> и одновременно попытаться его сохранить. Для импорта и массового создания нужна защита на уровне базы, API или очереди. Если текущая схема Bitrix не даёт подходящего ограничения, обработчик должен уметь повторить выбор кандидата после ошибки записи. Нельзя выдавать предварительную проверку за гарантию.</p>\n<p>Суффикс решает технический конфликт, но может быть плохим бизнес-правилом. Для артикулов, интеграционных ключей и SEO-адресов иногда нужен стабильный код из внешнего идентификатора. Тогда транслитерация названия остаётся только запасным способом для новых записей.</p>\n<p>Перевод на другой язык может создать другой код. Если один инфоблок связан с несколькими сайтами и языками, язык передавайте явно и заранее решите, общий ли код у витрин. Не смешивайте локальные коды в одном поле без правила обратной совместимости.</p>\n<p>Параметры и доступные методы зависят от версии Bitrix. На новых версиях в классе <code>CIBlockElement</code> есть методы работы с символьными кодами, но их применение зависит от настройки инфоблока. Перед заменой старого вызова сравните версию ядра, настройки поля и фактический компонент каталога.</p>\n<h2>Проверяемый критерий готовности</h2>\n<p>Решение готово, если два одинаковых названия в выбранной области получают разные коды, повторное сохранение существующего элемента сохраняет его код, пустое название получает проверенный резервный код, а URL открывает именно созданный ID. Отдельный тест должен показать отрицательный путь при конфликте параллельных операций. Для опубликованной записи старый URL либо продолжает вести на элемент, либо его поведение зафиксировано и проверено.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href=\"https://dev.1c-bitrix.ru/api_help/main/reference/cutil/translit.php\" target=\"_blank\" rel=\"noopener\">Документация 1С-Битрикс: CUtil::translit</a> — сигнатура и параметры транслитерации.</li><li><a href=\"https://dev.1c-bitrix.ru/api_help/iblock/classes/ciblockelement/index.php\" target=\"_blank\" rel=\"noopener\">Документация 1С-Битрикс: CIBlockElement</a> — методы выборки, добавления и изменения элементов, а также актуальные методы символьных кодов.</li><li><a href=\"https://dev.1c-bitrix.ru/api_help/main/general/urlrewrite.php\" target=\"_blank\" rel=\"noopener\">Документация 1С-Битрикс: обработка адресов</a> — правила URL, SEF-пути и связь публичного адреса со скриптом.</li></ul>"
|
||
}
|