Как вывести товары со скидками

Как вывести товары со скидками

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

Сначала в правилах работы с корзиной добавляем скидки на нужные товары и проставляем для них срок действия. Например, мы завели 3 правила: "Товары недели 1", "Товары недели 2", "Товары недели 3"
Теперь нужно вывести эти товары.

Часто для элементов каталога ставят свойство "Скидка" и делают выборку по этому свойству. Но нам такой вариант не подходит - слишком много ручной работы проставлять/снимать галочку для нужных товаров.

Вариант решения 1, самый простой:

заводим отдельный инфоблок с множественным свойством привязанные товары и/или разделы.

 Добавляем элемент "Товары недели 1", привязываем товары, которые будут со скидкой, и ставим даты активности, как в аналогичной скидке.

Теперь делаем наш слайдер: используем компонент bitrix:news.list. В параметрах в PROPERTY_CODE добавляем нужное нам свойство привязанных элементов, NEWS_COUNT я поставила 1, чтобы выбиралась одна активная запись.
<?$APPLICATION->IncludeComponent(
"bitrix:news.list",
"sale-slider",
Array(
...
"IBLOCK_ID" => "17",
"IBLOCK_TYPE" => "news",
"NEWS_COUNT" => "1",
...
"PROPERTY_CODE" => array("","PRODUCTS",""),
...
"SORT_BY1" => "ACTIVE_FROM",
"SORT_BY2" => "SORT",
"SORT_ORDER1" => "DESC",
"SORT_ORDER2" => "ASC",
)
);?>

Кастомизируем компонент


В result_modifier.php сохраняем массив ID товаров в $arResult['PRODUCTS'] и добавляем ключ в кэш
<?
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
$arResult['PRODUCTS']=[];
foreach($arResult["ITEMS"] as $item){
if(!empty($item["PROPERTIES"]["PRODUCTS"]["VALUE"])){ 
$arResult['PRODUCTS'] = array_merge($arResult['PRODUCTS'],$item['PROPERTIES']['PRODUCTS']['VALUE']);
}
}
if(!empty($arResult['PRODUCTS'])){
$arResult['PRODUCTS'] = array_unique($arResult['PRODUCTS']);
$this->getComponent()->setResultCacheKeys(['PRODUCTS']);
}


В template.php ничего не выводим, оставляем пустым.

Создаем файл component_epilog.php, где добавляем фильтр id наших товаров и подключаем компонент bitrix:catalog.section

<?
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();

if (!empty($arResult['PRODUCTS']) || !empty($arResult['SECTION']))
{
    $GLOBALS['arrProductFilter']=[];
if(!empty($arResult['PRODUCTS']))
$GLOBALS['arrProductFilter']['=ID']=$arResult['PRODUCTS'];
elseif(!empty($arResult['SECTION']))
$GLOBALS['arrProductFilter']['=SECTION_ID']=$arResult['SECTION'];

    $arCatalogSectionParams = array(
        'IBLOCK_ID' => "12",//IBLOCK_ID каталога товаров
        'IBLOCK_TYPE' => "catalog",
"CACHE_FILTER" => "Y",
"CACHE_GROUPS" => "Y",
"CACHE_TIME" => "3600",
"CACHE_TYPE" => "A",
        'FILTER_NAME' => 'arrProductFilter',
        'PRICE_CODE' => array(
0 => "BASE",
),
        'SHOW_ALL_WO_SECTION' => 'Y',
"SHOW_DISCOUNT_PERCENT" => "Y",
    );

    $APPLICATION->IncludeComponent(
            'bitrix:catalog.section',
            'slider-sale',
            $arCatalogSectionParams,
        false,
        array('HIDE_ICONS'=>true)
    );
}


Теперь этот список товаров будет показываться, пока не истечет дата активности новости, а потом подключится следующая активная.
Чтобы кэш инфоблока товары недели сбрасывался автоматически добавьте его в настройках модуля "Инфоблоки" на вкладке "Тегированный кэш". Подробнее тут.

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

Вариант 2, продвинутый

Используем тот же самый инфоблок, только вместо товаров добавляем свойство DISCOUNT - "ID скидки".
Идем в элемент "Товары недели 1" и ставим в это поле ID скидки, из которой мы будем брать товары.
Изменяем код в result_modifier.php нашего компонента. Теперь мы здесь получаем товары из скидки по ID
<?
if (!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED !== true) die();
$arResult['PRODUCTS']=[];
$arResult['SECTION']=[];
$discounts=[];
\Bitrix\Main\Loader::includeModule("sale");
//подключаем файл 
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/sale/handlers/discountpreset/simpleproduct.php");
foreach($arResult["ITEMS"] as $item){
if(!empty($item["PROPERTIES"]["DISCOUNT"]["VALUE"])){ 
$discounts[] = $item["PROPERTIES"]["DISCOUNT"]["VALUE"];
}
}
if(!empty($discounts)){
$dbProductDiscounts = \Bitrix\Sale\Internals\DiscountTable::getList([
'filter' => [
'ID' => $discounts,
],
'select' => [
"*"
]
]);
$discountObj = new Sale\Handlers\DiscountPreset\SimpleProduct();
while ($arProductDiscounts = $dbProductDiscounts->Fetch())
{
   $discount =  $discountObj->generateState($arProductDiscounts);
.    if(!empty($discount['discount_product'])){ 
$arResult['PRODUCTS'] = array_merge($arResult['PRODUCTS'],$discount['discount_product']);
}
,    unset($discount);
}

}
if(!empty($arResult['PRODUCTS']))
$this->getComponent()->setResultCacheKeys(['PRODUCTS']);


Все остальное оставляем без изменений - template.php пустой и в component_epilog.php подключается компонент с нашими товарами.

С этим кодом можно работать дальше: например, написать свой компонент, который будет с какой-то периодичностью проверять активные скидки и обновлять список товаров. Но мне вариант 2 показался удобным своей предсказуемостью: легко можно контролировать процесс вручную, поменять дату, переключить на другую скидку, сбросить кэш. Небольшое неудобство только в необходимости синхронизации дат активности скидок и новостей, которые отвечают за вывод.

21.12.2023

Возврат к списку