Сортировка в каталоге Битрикс. Сортировка по цене, свойству Новинка. Ajax

Вводные

Работаем с комплексным компонентом каталога. Товары лежат в корне, поэтому работаем в sections.php

Сортировать будем по цене (возрастание, убывание), по свойству "Новинка" и свойству "Набор".

Можно добавить любые другие свойства. По умолчанию будет применяться сортировка по свойству "Новинка".

Запоминаем выбор пользователя на 16 часов, записывая сортировку в cookie.

Обновляем товары без перезагрузки страницы.

sections.php

/**
 * Типы Цен и Сортировка
 */

$sortPrice = $arParams['ELEMENT_SORT_FIELD2'];

// Проверяем приоритеты:
// 1. AJAX запрос, 2. GET параметр, 3. Cookie, 4. По умолчанию.
$currentSort = 'property_NOVINKA'; // Значение по умолчанию.

// Проверяем AJAX запрос.
if ($_REQUEST['sort_ajax'] === 'Y' && $_REQUEST['sort']) {
    $currentSort = $_REQUEST['sort'];
}
// Проверяем GET параметр
else if ($_REQUEST['sort']) {
    $currentSort = $_REQUEST['sort'];
}
// Проверяем cookie (действительна 16 часов)
else if ($_COOKIE['catalog_sort_order']) {
    $currentSort = $_COOKIE['catalog_sort_order'];
}

// Применяем сортировку
switch ($currentSort) {
    case 'price_asc':
        $arParams['ELEMENT_SORT_FIELD'] = $sortPrice;
        $arParams['ELEMENT_SORT_ORDER'] = 'asc';
        $arParams['ELEMENT_SORT_FIELD2'] = 'ID';
        $arParams['ELEMENT_SORT_ORDER2'] = 'asc';
        break;

    case 'price_desc':
        $arParams['ELEMENT_SORT_FIELD'] = $sortPrice;
        $arParams['ELEMENT_SORT_ORDER'] = 'desc';
        $arParams['ELEMENT_SORT_FIELD2'] = 'ID';
        $arParams['ELEMENT_SORT_ORDER2'] = 'desc';
        break;
    case 'set':
        $arParams['ELEMENT_SORT_FIELD'] = 'property_KOMPLEKTNOST';
        $arParams['ELEMENT_SORT_ORDER'] = 'desc,nulls';
        $arParams['ELEMENT_SORT_FIELD2'] = 'name';
        $arParams['ELEMENT_SORT_ORDER2'] = 'asc';
        break;
    case 'new':
    default:
        $arParams['ELEMENT_SORT_FIELD'] = 'property_NOVINKA';
        $arParams['ELEMENT_SORT_ORDER'] = 'desc';
        $arParams['ELEMENT_SORT_FIELD2'] = $sortPrice;
        $arParams['ELEMENT_SORT_ORDER2'] = 'asc';
        break;
}

$arResult['CURRENT_SORT'] = $currentSort;

html

<div class="sorting__box">
    <form id="sortForm" action="<?=$APPLICATION->GetCurPage()?>" method="get" class="sorting-form">
        <div class="sorting">
            <div class="select__box sorting-box custom-select">
                <select class="form-select" name="sort" onchange="submitFormSorting()">
                    <option value="new" <?=($arResult['CURRENT_SORT'] == 'new') ? 'selected' : ''?>>Новинки</option>
                    <option value="price_asc" <?=($arResult['CURRENT_SORT'] == 'price_asc') ? 'selected' : ''?>>По цене (возрастание)</option>
                    <option value="price_desc" <?=($arResult['CURRENT_SORT'] == 'price_desc') ? 'selected' : ''?>>По цене (убывание)</option>
                    <option value="set" <?=($arResult['CURRENT_SORT'] == 'set') ? 'selected' : ''?>>По гарнитуру</option>
                </select>
            </div>
        </div>

        <?php
        // Сохраняем другие GET параметры
        foreach ($_GET as $key => $value) {
            if ($key !== 'sort' && $key !== 'sort_ajax') {
                if (is_array($value)) {
                    foreach ($value as $item) {
                        echo '<input type="hidden" name="'.htmlspecialcharsbx($key).'[]" value="'.htmlspecialcharsbx($item).'">';
                    }
                } else {
                    echo '<input type="hidden" name="'.htmlspecialcharsbx($key).'" value="'.htmlspecialcharsbx($value).'">';
                }
            }
        }
        ?>
    </form>
</div>

sections.php

В этом файле, для реализации обновления товаров без перезагрузки, нам нужном обрамить компонент catalog.section следующим кодом

<div class="catalog-products" id="catalogProducts">
    <?
    // Для сортировки
    if ($_REQUEST['sort_ajax'] === 'Y')  {
        $APPLICATION->RestartBuffer();
    }
    ?>

    <? $APPLICATION->IncludeComponent(
        "bitrix:catalog.section",
        'section',
        [
            "IBLOCK_TYPE" => $arParams["IBLOCK_TYPE"],
            "IBLOCK_ID" => $arParams["IBLOCK_ID"],
            "ELEMENT_SORT_FIELD" => $arParams["ELEMENT_SORT_FIELD"],
            "ELEMENT_SORT_ORDER" => $arParams["ELEMENT_SORT_ORDER"],
            "ELEMENT_SORT_FIELD2" => $arParams["ELEMENT_SORT_FIELD2"],
            "ELEMENT_SORT_ORDER2" => $arParams["ELEMENT_SORT_ORDER2"],
            // ...
        ],
        $component
    );
    ?>
    <?
    // Сортировка
    if ($_REQUEST['sort_ajax'] === 'Y')  {
        die();
    }
    ?>
</div>

script.js

Добавляем обработчик на форму выбора сортировки.

/**
 * Сортировка в каталоге (AJAX)
 */
function submitFormSorting() {
    const form = document.getElementById('sortForm');
    const select = form.querySelector('select[name="sort"]');
    const selectedValue = select.value;
    const cookieName = 'catalog_sort_order';
    const catalogSectionBlock = document.querySelector('#catalogProducts');

    // Сохраняем выбранную сортировку в cookie
    // BX.setCookie(cookieName, selectedValue, {expires: 86400*365}); // 365 дней
    BX.setCookie(cookieName, selectedValue, {expires: 57600, path: '/'}); // 16 часов

    if (catalogSectionBlock) {
        toggleLoader(catalogSectionBlock); // Показываем лоадер

        const formData = new FormData();
        formData.append('sort', selectedValue);
        formData.append('sort_ajax', 'Y'); // Флаг AJAX-запроса

        // Сохраняем другие параметры URL
        const urlParams = new URLSearchParams(window.location.search);
        for (const [key, value] of urlParams) {
            if (key !== 'sort') {
                formData.append(key, value);
            }
        }

        fetch(window.location.href, {
            method: 'POST',
            body: formData,
            headers: {
                'X-Requested-With': 'XMLHttpRequest' // Для обработки на сервере
            }
        })
        .then(response => response.text())
        .then(response => {
            catalogSectionBlock.innerHTML = response;

            // Обработка скриптов в ответе
            const ob = BX.processHTML(response);
            BX.ajax.processScripts(ob.SCRIPT);

            // Генерируем кастомное событие
            BX.onCustomEvent('onCatalogSortChanged', [{
                block: catalogSectionBlock,
                sortValue: selectedValue
            }]);

            toggleLoader(catalogSectionBlock, 'remove'); // Скрываем лоадер
        })
        .catch(error => {
            toggleLoader(catalogSectionBlock, 'remove');
            console.error('Sorting error:', error);
        });
    }
}

// Установка текущего значения при загрузке страницы
document.addEventListener('DOMContentLoaded', () => {
    const cookieName = 'catalog_sort_order';
    const cookieValue = document.cookie
    .split('; ')
    .find(row => row.startsWith(cookieName))
    ?.split('=')[1];

    if (cookieValue) {
        const select = document.querySelector('#sortForm select[name="sort"]');
        if (select) {
            select.value = cookieValue;
        }
    }
});