Уникальный URL для торговых предложений (SKU) с возможностью автоматического выбора предложения при переходе по ссылке.

Просмотров: 2310

Задача: на странице товара необходимо реализовать функцию автоматического изменения URL при выборе определенной товарной позиции без перезагрузки страницы. При этом, если ссылка скопирована и передана другому пользователю, должен осуществляться автоматический выбор соответствующей товарной позиции при переходе по ссылке.

Уникальный URL для торговый предложений (SKU) с возможностью автоматического выбора предложения при переходе по ссылке.

Подмена url с помощью javascript, без перезагрузки страницы

Для начала необходимо открыть файл script.js шаблона детального просмотра элемента и найти участок, который отвечает за переключение SKU. Обычно это строка selectOfferProp: function() на, примерно, 1792-ой строке.

Полный код модифицированной функции ниже, с комментариями-отбивками ("//start CUSTOM" и "// end CUSTOM"):

selectOfferProp: function () {
    var i = 0,
        strTreeValue = '',
        arTreeItem = [],
        rowItems = null,
        target = BX.proxy_context,
        smallCardItem;

    if (target && target.hasAttribute('data-treevalue')) {
        if (BX.hasClass(target, 'selected'))
            return;

        if (typeof document.activeElement === 'object') {
            document.activeElement.blur();
        }
        
        // start CUSTOM
        var skuId = this.offers[this.offerNum]['ID'];
        // end CUSTOM
        
        strTreeValue = target.getAttribute('data-treevalue');
        arTreeItem = strTreeValue.split('_');
        this.searchOfferPropIndex(arTreeItem[0], arTreeItem[1]);

        rowItems = BX.findChildren(target.parentNode, {
            tagName: 'li'
        }, false);

        if (rowItems && rowItems.length) {
            for (i = 0; i < rowItems.length; i++) {
                BX.removeClass(rowItems[i], 'selected');
            }

            // start CUSTOM
            var currentUrl = window.location.href;
            var newUrl = currentUrl.replace(skuId + '/', '');
            window.history.replaceState(null, null, newUrl);
            // end CUSTOM

        }

        BX.addClass(target, 'selected');

        // start CUSTOM
        var skuId = this.offers[this.offerNum]['ID'];
        var newUrl = window.location.pathname + skuId + '/';
        window.history.pushState('', '', newUrl);
        // end CUSTOM

        if (this.smallCardNodes.panel) {
            smallCardItem = this.smallCardNodes.panel.querySelector('[data-treevalue="' + strTreeValue + '"]');
            if (smallCardItem) {
                rowItems = this.smallCardNodes.panel.querySelectorAll('[data-sku-line="' + smallCardItem.getAttribute('data-sku-line') + '"]');
                for (i = 0; i < rowItems.length; i++) {
                    rowItems[i].style.display = 'none';
                }

                smallCardItem.style.display = '';
            }
        }

        if (
            this.isFacebookConversionCustomizeProductEventEnabled &&
            BX.Type.isArrayFilled(this.offers) &&
            BX.Type.isObject(this.offers[this.offerNum])
        ) {
            BX.ajax.runAction(
                'sale.facebookconversion.customizeProduct', {
                    data: {
                        offerId: this.offers[this.offerNum]['ID']
                    }
                }
            );
        }
    }
},
  • Создаем переменную skuId, в которой хранится ID выбранного торгового предложения.
  • С помощью метода window.history.replaceState очищаем URL от ранее вставленного ID предложения.
  • С помощью метода window.history.pushState добавляем в URL ID выбранного предложения с учетом ЧПУ.

Теперь, при переключении предложений, в URL будет добавляться ID элемента торгового предложения с учетом ЧПУ. Если в URL уже есть ID предложения, оно будет удалено и добавится выбранное предложение.

Все будет происходить без перезагрузки страницы, и пользователь, при желании, сможет скопировать адрес, чтобы поделиться прямой ссылкой на предложение.

Прямая ссылка на торговое предложение

Если пользователь скопирует сгенерированную ссылку и поделится ей с другом, друг попадет на страницу с ошибкой 404, так как для системы таких URL не существует.

Чтобы обработать этот переход и направить пользователя в карточку товара, нужно установить вот такие параметры обработки ошибки 404, в настройках комплексного компонента каталога.

URL для торговый предложений (SKU)

В директории каталога создаем файл 404.php со следующим содержимым:

include_once($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/urlrewrite.php');
$currentPath = GetPagePath();
$arUrl = explode('/', $currentPath);
// Находим инфоблок предложений
$dbIBlock = CIBlock::GetList(
    false,
    array(
        "TYPE" => "offers",
        "SITE_ID" => SITE_ID,
    )
);
if ($arIBlock = $dbIBlock->GetNext()) {
    $offersIblocId = $arIBlock['ID'];
}
// Проходим по предложениям в посках ID из url
$dbOffers = CIBlockElement::GetList(
    false,
    array(
        'IBLOCK_ID' => $offersIblocId
    ),
    false,
    false,
    array('ID')
);
while ($arOffers = $dbOffers->Fetch()) {
    if (in_array($arOffers['ID'], $arUrl)) {
        $removePath = $arOffers['ID'] . '/';
        $urlWithoutOfferId = str_replace($removePath, '', $currentPath);
        LocalRedirect($urlWithoutOfferId . '?TARGET_OFFER=' . $arOffers['ID']); // отправляем в товар
        exit;
    }
}

CHTTP::SetStatus("404 Not Found");
@define("ERROR_404", "Y");
define("HIDE_SIDEBAR", true);

require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/header.php");

$APPLICATION->SetTitle("Страница не найдена");

//  !!!!! Тут ваша красивая 404 ошибка каталога

require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/footer.php");
  • Получаем текущий URL с помощью $_SERVER['REQUEST_URI'] и разбиваем его на массив с помощью функции explode().
  • Далее, с помощью CIBlock::GetList() получаем ID инфоблока с предложениями. Можно просто указать его в CIBlockElement::GetList().
  • С помощью CIBlockElement::GetList() определяем, есть ли в URL ID предложения.
  • Если совпадение найдено, мы вырезаем ID предложения из URL и перенаправляем пользователя на страницу товара (без учета предложения), но с GET-параметром TARGET_OFFER, в котором указан ID предложения.
  • Если это несуществующий ID предложения или какая-то неправильная строка в URL, условие не сработает и отдастся обычная 404 ошибка.

Выбираем торговое предложение зная его ID

Теперь, когда пользователь перешел по ссылке на товар с GET параметром, в котором хранится ID торгового предложения, нужно это обработать и открыть ему ссылку с выбранным предложением.

В файл component_epilog.php, в шаблоне catalog.element добавляем:

if (!empty($_REQUEST['TARGET_OFFER'])) {
    $offerNum = array_search($_REQUEST['TARGET_OFFER'], $templateData['OFFER_IDS']);
    if (!empty($offerNum))
    {?>
        <script>
            BX.ready(function(){
                if (!!window.)
                {
                   window.<?=$templateData['JS_OBJ']?>.setOffer(<?=$offerNum?>);
                }

                const url = window.location.href.replace(/(.*\/.*\/.*\/.*)\?TARGET_OFFER=(.*)/, '$1$2/');
                const title = document.title;
                window.history.replaceState({path: url}, title, url);

            });
        </script>
             <?
    }
}
  • Проверили, не является ли запрос с TARGET_OFFER пустым.
  • С помощью array_search нашли ключ, в котором хранится ID предложения из GET параметра.
  • Установили выбор на предложении из GET параметра с помощью window.setOffer.
  • Используя window.history.replaceState, перезаписали URL, чтобы вместо GET параметра был ЧПУ вариант ссылки.
Заметка написана без учета SEO. Для поисковиков не существуют страницы для каждого предложения.
У заметки есть продолжение: Уникальная СЕО информация для каждого торгового предложения

Видео: Как установить свой URL для торговых предложений (SKU) в Битрикс

Более наглядно и с учетом нюансов описанных выше, можно посмотреть в видео. Полная версия на моем BOOSTY канале, подписка всего 23 рубля в месяц

$OFFER_ID = filter_input(INPUT_GET, 'OFFER_ID', FILTER_SANITIZE_NUMBER_INT); - что бы исключить SQL инъекции и сделать работу более безопасной.

Михаил Базаров 15.04.2023
Любые вопросы и уточнения по этой заметке пишите в комментарии.
Есть вероятность, что я чо-то не учел.
Торговые площадки типа ОЗОН и ЯндексМаркет переваривают это на раз и их все устраивает.
Не забывайте в инфоблоке ТП прописать полный путь до ТП - если нужна выгрузка в маркетплейсы.

Если вам достаточно что бы URL были вида /catalog/товар/?TARGET_OFFER=123
Можете не запариваться с 404.php
Достаточно внести правки только в шаблон детальной карточки
в script.js и component_epilog.php
Гость 17.04.2023
Было бы отлично увидеть вариант с учетом SEO