Получить DETAIL_PAGE_URL. Url раздела, элемента в Битрикс D7

Технические аспекты формирования URL

Для корректной генерации адресов необходимо учитывать:

  1. Настройки ЧПУ в параметрах инфоблока
  2. Шаблоны URL в urlrewrite.php
  3. Данные элемента/раздела, используемые в макросах 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' - раздел

Особенности реализации

  1. Многосайтовость\ Обязательно включать в выборку:

    • IBLOCK_SITE.SITE_DIR
    • IBLOCK_SITE.LANG_DIR
  2. Кастомные шаблоны\ При использовании пользовательских макросов в URL добавляйте соответствующие поля:

    'select' => [
       'PROPERTY_CUSTOM_URL_CODE' // Для макроса #CUSTOM_URL_CODE#
    ]
  3. Обработка вложенных разделов\ Для многоуровневой структуры:

    $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']
           )
       ]
    ]);

Оптимизация производительности

  1. Кэширование запросов\ Используйте статическое кэширование для часто запрашиваемых элементов:

    \Bitrix\Main\Data\StaticCache::create(
       'element_url_' . $elementId,
       $elementData,
       3600 // TTL 1 час
    );
  2. Пакетная обработка\ Для массовой генерации 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("'(?