Технические аспекты формирования URL
Для корректной генерации адресов необходимо учитывать:
- Настройки ЧПУ в параметрах инфоблока
- Шаблоны URL в
urlrewrite.php - Данные элемента/раздела, используемые в макросах URL
Получение URL элемента
1. Инициализация Data-класса
Используйте динамическое получение класса сущности:
use Bitrix\Iblock\Iblock;
$iblockId = 15; // ID целевого инфоблока
$entityClass = Iblock::wakeUp($iblockId)->getEntityDataClass();
2. Выборка данных элемента
Критически важные поля для генерации URL
(нам нужно выбирать все те поля, которые используются в настройках инфоблока, компонента и urlrewrite):
$elementData = $entityClass::getRow([
'select' => [
'ID',
'IBLOCK_ID',
'CODE',
'IBLOCK_SECTION_ID',
'NAME',
'DETAIL_PAGE_URL' => 'IBLOCK.DETAIL_PAGE_URL',
'LANG_DIR' => 'IBLOCK_SITE.LANG_DIR', // Для многосайтовости
'SITE_DIR' => 'IBLOCK_SITE.SITE_DIR' // Корень сайта
],
'filter' => ['=ID' => $elementId]
]);
3. Генерация конечного URL
if ($elementData) {
$detailUrl = \CIBlock::ReplaceDetailUrl(
$elementData['DETAIL_PAGE_URL'],
$elementData,
false, // Не использовать кэш
'E' // Тип сущности: Element
);
// Формирование абсолютного URL
$absoluteUrl =
$elementData['SITE_DIR'] .
$elementData['LANG_DIR'] .
$detailUrl;
}
Получение URL раздела
1. Выборка базовых параметров
use Bitrix\Iblock\SectionTable;
$sectionData = SectionTable::getRow([
'select' => [
'ID',
'IBLOCK_ID',
'CODE',
'NAME',
'IBLOCK_SECTION_ID',
'SECTION_PAGE_URL' => 'IBLOCK.SECTION_PAGE_URL',
'LANG_DIR' => 'IBLOCK.SITE.LANG_DIR',
'SITE_DIR' => 'IBLOCK.SITE.SITE_DIR'
],
'filter' => ['=ID' => $sectionId]
]);
2. Обработка URL
if ($sectionData) {
$sectionUrl = \CIBlock::ReplaceDetailUrl(
$sectionData['SECTION_PAGE_URL'],
$sectionData,
true, // Использовать кэширование
'S' // Тип сущности: Section
);
$absoluteSectionUrl =
$sectionData['SITE_DIR'] .
$sectionData['LANG_DIR'] .
$sectionUrl;
}
Ключевые параметры метода ReplaceDetailUrl
| Параметр | Тип | Описание |
|---|---|---|
$url |
string | Шаблон URL из настроек инфоблока |
$fields |
array | Массив данных элемента/раздела |
$bCache |
bool | Флаг кэширования (рекомендуется true для статичных данных) |
$type |
string | Тип объекта: 'E' - элемент, 'S' - раздел |
Особенности реализации
-
Многосайтовость\ Обязательно включать в выборку:
IBLOCK_SITE.SITE_DIRIBLOCK_SITE.LANG_DIR
-
Кастомные шаблоны\ При использовании пользовательских макросов в URL добавляйте соответствующие поля:
'select' => [ 'PROPERTY_CUSTOM_URL_CODE' // Для макроса #CUSTOM_URL_CODE# ] -
Обработка вложенных разделов\ Для многоуровневой структуры:
$sectionData = SectionTable::getList([ 'select' => [ 'ID', 'CODE', 'IBLOCK_SECTION_ID', 'SECTION_PATH' => 'PATH.DIRECTORY_PATH' ], 'runtime' => [ new \Bitrix\Main\Entity\ReferenceField( 'PATH', '\Bitrix\Iblock\SectionPathTable', ['=this.ID' => 'ref.SECTION_ID'] ) ] ]);
Оптимизация производительности
-
Кэширование запросов\ Используйте статическое кэширование для часто запрашиваемых элементов:
\Bitrix\Main\Data\StaticCache::create( 'element_url_' . $elementId, $elementData, 3600 // TTL 1 час ); -
Пакетная обработка\ Для массовой генерации URL:
$elements = $entityClass::getList([ 'select' => ['ID', 'CODE', 'IBLOCK_SECTION_ID'], 'filter' => ['@ID' => $elementIds] ]); while ($element = $elements->fetch()) { $urls[$element['ID'] = $this->generateUrl($element); }
Для элемента
use Bitrix\Iblock\Elements\ElementCatalogTable;
$rawData = ElementCatalogTable::getRow([
'select' => [
'ID',
'IBLOCK_SECTION_ID',
'CODE',
'NAME',
'IBLOCK_ID',
'DETAIL_PAGE_URL' => 'IBLOCK.DETAIL_PAGE_URL',
],
'filter' => [
'=CODE' => 'some code',
],
]);
if (null !== $rawData) {
$rawData['DETAIL_PAGE_URL'] = CIBlock::ReplaceDetailUrl(
$rawData['DETAIL_PAGE_URL'],
$rawData,
false,
'E'
);
}
Для раздела
use Bitrix\Main\Loader;
use Bitrix\Iblock\SectionTable;
$sectionList = [];
$rawData = SectionTable::getList([
'select' => [ // Собираем select из тех полей, на которых собирается наш ЧПУ.
'ID',
'NAME',
'CODE',
'IBLOCK_SECTION_ID',
'SECTION_PAGE_URL_RAW' => 'IBLOCK.SECTION_PAGE_URL',
],
'filter' => [
'=IBLOCK_ID' => $arParams['IBLOCK_ID'],
],
]);
foreach ($rawData as $section) {
$sectionList[$section['ID']] = [
'ID' => $section['ID'],
'NAME' => $section['NAME'],
'SECTION_PAGE_URL' => CIBlock::ReplaceDetailUrl($section['SECTION_PAGE_URL_RAW'], $section, true, 'S'),
];
Метод ReplaceDetailUrl
/bitrix/modules/iblock/classes/general/iblock.php
static function ReplaceDetailUrl($url, $arr, $server_name = false, $arrType = false)
{
$url = (string)$url;
if ($server_name)
{
$url = str_replace('#LANG#', (string)($arr['LANG_DIR'] ?? ''), $url);
if (
(defined('ADMIN_SECTION') && ADMIN_SECTION === true)
|| !defined('BX_STARTED')
)
{
static $cache = array();
if (isset($arr['LID']))
{
if (!isset($cache[$arr['LID']]))
{
$db_lang = CLang::GetByID($arr['LID']);
$arLang = $db_lang->Fetch();
if (!empty($arLang))
{
$arLang['DIR'] = (string)$arLang['DIR'];
$arLang['SERVER_NAME'] = (string)$arLang['SERVER_NAME'];
}
$cache[$arr['LID']] = $arLang;
}
$arLang = $cache[$arr['LID']];
if (!empty($arLang))
{
$url = str_replace(
[
'#SITE_DIR#',
'#SERVER_NAME#',
],
[
$arLang['DIR'],
$arLang['SERVER_NAME'],
],
$url
);
}
}
}
else
{
$url = str_replace(
[
'#SITE_DIR#',
'#SERVER_NAME#',
],
[
SITE_DIR,
SITE_SERVER_NAME,
],
$url
);
}
}
$id = (int)($arr['ID'] ?? 0);
$preparedId = $id > 0 ? $id : '';
static $arSearch = [
/*Thees come from GetNext*/
'#SITE_DIR#',
'#ID#',
'#CODE#',
'#EXTERNAL_ID#',
'#IBLOCK_TYPE_ID#',
'#IBLOCK_ID#',
'#IBLOCK_CODE#',
'#IBLOCK_EXTERNAL_ID#',
/*And thees was born during components 2 development*/
'#ELEMENT_ID#',
'#ELEMENT_CODE#',
'#SECTION_ID#',
'#SECTION_CODE#',
'#SECTION_CODE_PATH#',
];
$iblockId = (int)($arr['IBLOCK_ID'] ?? 0);
$preparedCode = rawurlencode(
(string)($arr['~CODE'] ?? ($arr['CODE'] ?? ''))
);
$iblockSectionId = (int)($arr['IBLOCK_SECTION_ID'] ?? 0);
$arReplace = [
(string)($arr['LANG_DIR'] ?? ''),
$preparedId,
$preparedCode,
rawurlencode(
(string)($arr['~EXTERNAL_ID'] ?? ($arr['EXTERNAL_ID'] ?? ''))
),
rawurlencode(
(string)($arr['~IBLOCK_TYPE_ID'] ?? ($arr['IBLOCK_TYPE_ID'] ?? ''))
),
($iblockId > 0 ? $iblockId : ''),
rawurlencode(
(string)($arr['~IBLOCK_CODE'] ?? ($arr['IBLOCK_CODE'] ?? ''))
),
rawurlencode(
(string)($arr['~IBLOCK_EXTERNAL_ID'] ?? ($arr['IBLOCK_EXTERNAL_ID'] ?? ''))
),
];
if ($arrType === "E")
{
if (strpos($url, '#PRODUCT_URL#') !== false)
{
$url = str_replace(
'#PRODUCT_URL#',
self::getProductUrlValue($arr, $server_name),
$url
);
}
$arReplace[] = $preparedId;
$arReplace[] = rawurlencode(
(string)($arr['~CODE'] ?? ($arr['CODE'] ?? ''))
);
#Deal with symbol codes
$SECTION_CODE = '';
$SECTION_CODE_PATH = '';
if ($iblockSectionId > 0)
{
if (strpos($url, '#SECTION_CODE#') !== false)
{
$SECTION_CODE = CIBlockSection::getSectionCode($iblockSectionId);
}
if (strpos($url, '#SECTION_CODE_PATH#') !== false)
{
$SECTION_CODE_PATH = CIBlockSection::getSectionCodePath($iblockSectionId);
}
}
$arReplace[] = $iblockSectionId > 0 ? $iblockSectionId: '';
$arReplace[] = $SECTION_CODE;
$arReplace[] = $SECTION_CODE_PATH;
}
elseif ($arrType === "S")
{
$SECTION_CODE_PATH = '';
if (
$id > 0
&& strpos($url, '#SECTION_CODE_PATH#') !== false
)
{
$SECTION_CODE_PATH = CIBlockSection::getSectionCodePath($id);
}
$arReplace[] = '';
$arReplace[] = '';
$arReplace[] = $preparedId;
$arReplace[] = $preparedCode;
$arReplace[] = $SECTION_CODE_PATH;
}
else
{
$elementId = (int)($arr['ELEMENT_ID'] ?? 0);
$preparedElementId = $elementId > 0 ? $elementId : '';
$arReplace[] = $preparedElementId;
$arReplace[] = rawurlencode((string)($arr['~ELEMENT_CODE'] ?? ($arr['ELEMENT_CODE'] ?? '')));
$arReplace[] = $iblockSectionId > 0 ? $iblockSectionId : '';
$arReplace[] = rawurlencode((string)($arr['~SECTION_CODE'] ?? ($arr['SECTION_CODE'] ?? '')));
$arReplace[] = '';
}
$url = str_replace($arSearch, $arReplace, $url);
return preg_replace("'(?